纯真IP数据库查询,C#.NET实现。

参看了,LumaQQ 作者对数据库文件分析的文章,和实现代码,改写的C#.NET版本,感谢原作者。


/*******************************************
 * 说明:查询纯真IP数据库,数据库来自CZ88.NET(纯真网络),感谢 LumaQQ 作者:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
 * 作者:孙宇
 * 日期:2011/11/08
/*******************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;

namespace CZIPDemo
{
    /// 
    /// 存储地区的结构
    /// 
    public struct stLocation
    {
        /// 
        /// 未使用
        /// 
        public string Ip;

        /// 
        /// 国家名
        /// 
        public string Contry;

        /// 
        /// 城市名
        /// 
        public string City;
    }


    /// 
    /// 纯真IP数据库查询辅助类
    /// 
    public static class QqwryHelper
    {
        #region 成员变量

        private const byte REDIRECT_MODE_1 = 0x01;//名称存储模式一
        private const byte REDIRECT_MODE_2 = 0x02;//名称存储模式二
        private const int IP_RECORD_LENGTH = 7; //每条索引的长度

        private static long beginIndex = 0;//索引开始
        private static long endIndex = 0;//索引结束

        private static stLocation loc = new stLocation() { City = "未知城市", Contry = "未知国家" };

        private static Stream fs;

        #endregion

        #region 私有成员函数

        /// 
        /// 在索引区查找指定IP对应的记录区地址
        /// 
        /// 字节型IP
        /// 
        private static long SearchIpIndex(byte[] _ip)
        {
            long index = 0;

            byte[] nextIp = new byte[4];

            ReadIp(beginIndex, ref nextIp);

            int flag = CompareIp(_ip, nextIp);
            if (flag == 0) return beginIndex;
            else if (flag < 0) return -1;

            for (long i = beginIndex, j = endIndex; i < j; )
            {
                index = GetMiddleOffset(i, j);

                ReadIp(index, ref nextIp);
                flag = CompareIp(_ip, nextIp);

                if (flag == 0) return ReadLong(index + 4, 3);
                else if (flag > 0) i = index;
                else if (flag < 0)
                {
                    if (index == j)
                    {
                        j -= IP_RECORD_LENGTH;
                        index = j;
                    }
                    else
                    {
                        j = index;
                    }
                }
            }

            index = ReadLong(index + 4, 3);
            ReadIp(index, ref nextIp);

            flag = CompareIp(_ip, nextIp);
            if (flag <= 0) return index;
            else return -1;
        }

        /// 
        /// 获取两个索引的中间位置
        /// 
        /// 索引1
        /// 索引2
        /// 
        private static long GetMiddleOffset(long begin, long end)
        {
            long records = (end - begin) / IP_RECORD_LENGTH;
            records >>= 1;
            if (records == 0) records = 1;
            return begin + records * IP_RECORD_LENGTH;
        }
        
        /// 
        /// 读取记录区的地区名称
        /// 
        /// 位置
        /// 
        private static string ReadString(long offset)
        {
            fs.Position = offset;

            byte b = (byte)fs.ReadByte();
            if (b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2)
            {
                long areaOffset = ReadLong(offset + 1, 3);
                if (areaOffset == 0)
                    return "未知地区";

                else fs.Position = areaOffset;
            }
            else
            {
                fs.Position = offset;
            }

            List buf = new List();

            int i = 0;
            for (i = 0, buf.Add((byte)fs.ReadByte()); buf[i] != (byte)(0); ++i, buf.Add((byte)fs.ReadByte())) ;

            if (i > 0) return Encoding.Default.GetString(buf.ToArray(), 0, i);
            else return "";
        }

        /// 
        /// 从自定位置读取指定长度的字节,并转换为big-endian字节序(数据源文件为little-endian字节序)
        /// 
        /// 开始读取位置
        /// 读取长度
        /// 
        private static long ReadLong(long offset, int length)
        {
            long ret = 0;
            fs.Position = offset;
            for (int i = 0; i < length; i++)
            {
                ret |= ((fs.ReadByte() << (i * 8)) & (0xFF * ((int)Math.Pow(16, i * 2))));
            }

            return ret;
        }

        /// 
        /// 从指定位置处读取一个IP
        /// 
        /// 指定的位置
        /// 保存IP的缓存区
        private static void ReadIp(long offset, ref byte[] _buffIp)
        {
            fs.Position = offset;
            fs.Read(_buffIp, 0, _buffIp.Length);

            for (int i = 0; i < _buffIp.Length / 2; i++)
            {
                byte temp = _buffIp[i];
                _buffIp[i] = _buffIp[_buffIp.Length - i - 1];
                _buffIp[_buffIp.Length - i - 1] = temp;
            }
        }

        /// 
        /// 比较两个IP是否相等,1:IP1大于IP2,-1:IP1小于IP2,0:IP1=IP2
        /// 
        /// IP1
        /// IP2
        /// 
        private static int CompareIp(byte[] _buffIp1, byte[] _buffIp2)
        {
            if (_buffIp1.Length > 4 || _buffIp2.Length > 4) throw new Exception("指定的IP无效。");

            for (int i = 0; i < 4; i++)
            {
                if ((_buffIp1[i] & 0xFF) > (_buffIp2[i] & 0xFF)) return 1;
                else if ((_buffIp1[i] & 0xFF) < (_buffIp2[i] & 0xFF)) return -1;
            }

            return 0;
        }

        /// 
        /// 从指定的地址获取区域名称
        /// 
        /// 
        private static void GetAreaName(long offset)
        {
            fs.Position = offset + 4;
            long flag = fs.ReadByte();
            long contryIndex = 0;
            if (flag == REDIRECT_MODE_1)
            {
                contryIndex = ReadLong(fs.Position, 3);
                fs.Position = contryIndex;

                flag = fs.ReadByte();

                if (flag == REDIRECT_MODE_2)    //是否仍然为重定向
                {
                    loc.Contry = ReadString(ReadLong(fs.Position, 3));
                    fs.Position = contryIndex + 4;
                }
                else
                {
                    loc.Contry = ReadString(contryIndex);
                }
                loc.City = ReadString(fs.Position);
            }
            else if (flag == REDIRECT_MODE_2)
            {
                contryIndex = ReadLong(fs.Position, 3);
                loc.Contry = ReadString(contryIndex);
                loc.City = ReadString(contryIndex + 3);
            }
            else
            {
                loc.Contry = ReadString(offset + 4);
                loc.City = ReadString(fs.Position);
            }
        }

        #endregion

        #region 公有成员函数

        /// 
        /// 加载数据库文件到缓存
        /// 
        /// 数据库文件地址
        /// 
        public static void Init(string path)
        {
            fs = new FileStream(path, FileMode.Open);
        }

        /// 
        /// 根据IP获取区域名
        /// 
        /// 指定的IP
        /// 
        public static stLocation GetLocation(string ip)
        {
            IPAddress ipAddress = null;
            if (!IPAddress.TryParse(ip, out ipAddress)) throw new Exception("无效的IP地址。");

            byte[] buff_local_ip = ipAddress.GetAddressBytes();

            beginIndex = ReadLong(0, 4);
            endIndex = ReadLong(4, 4);

            long offset = SearchIpIndex(buff_local_ip);
            if (offset != -1)
            {
                GetAreaName(offset);
            }

            loc.Contry = loc.Contry.Trim();
            loc.City = loc.City.Trim().Replace("CZ88.NET", "");

            return loc;
        }

        /// 
        /// 释放资源
        /// 
        public static void Dispose()
        {
            fs.Dispose();
        }

        #endregion
    }
}


你可能感兴趣的:(.NET)