纯真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  
{  
    /// <summary>  
    /// 存储地区的结构  
    /// </summary>  
    public struct stLocation  
    {  
        /// <summary>  
        /// 未使用  
        /// </summary>  
        public string Ip;  
  
        /// <summary>  
        /// 国家名  
        /// </summary>  
        public string Contry;  
  
        /// <summary>  
        /// 城市名  
        /// </summary>  
        public string City;  
    }  
  
  
    /// <summary>  
    /// 纯真IP数据库查询辅助类  
    /// </summary>  
    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 私有成员函数  
  
        /// <summary>  
        /// 在索引区查找指定IP对应的记录区地址  
        /// </summary>  
        /// <param name="_ip">字节型IP</param>  
        /// <returns></returns>  
        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;  
        }  
  
        /// <summary>  
        /// 获取两个索引的中间位置  
        /// </summary>  
        /// <param name="begin">索引1</param>  
        /// <param name="end">索引2</param>  
        /// <returns></returns>  
        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;  
        }  
          
        /// <summary>  
        /// 读取记录区的地区名称  
        /// </summary>  
        /// <param name="offset">位置</param>  
        /// <returns></returns>  
        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<byte> buf = new List<byte>();  
  
            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 "";  
        }  
  
        /// <summary>  
        /// 从自定位置读取指定长度的字节,并转换为big-endian字节序(数据源文件为little-endian字节序)  
        /// </summary>  
        /// <param name="offset">开始读取位置</param>  
        /// <param name="length">读取长度</param>  
        /// <returns></returns>  
        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;  
        }  
  
        /// <summary>  
        /// 从指定位置处读取一个IP  
        /// </summary>  
        /// <param name="offset">指定的位置</param>  
        /// <param name="_buffIp">保存IP的缓存区</param>  
        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;  
            }  
        }  
  
        /// <summary>  
        /// 比较两个IP是否相等,1:IP1大于IP2,-1:IP1小于IP2,0:IP1=IP2  
        /// </summary>  
        /// <param name="_buffIp1">IP1</param>  
        /// <param name="_buffIp2">IP2</param>  
        /// <returns></returns>  
        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;  
        }  
  
        /// <summary>  
        /// 从指定的地址获取区域名称  
        /// </summary>  
        /// <param name="offset"></param>  
        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 公有成员函数  
  
        /// <summary>  
        /// 加载数据库文件到缓存  
        /// </summary>  
        /// <param name="path">数据库文件地址</param>  
        /// <returns></returns>  
        public static void Init(string path)  
        {  
            fs = new FileStream(path, FileMode.Open);  
        }  
  
        /// <summary>  
        /// 根据IP获取区域名  
        /// </summary>  
        /// <param name="ip">指定的IP</param>  
        /// <returns></returns>  
        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;  
        }  
  
        /// <summary>  
        /// 释放资源  
        /// </summary>  
        public static void Dispose()  
        {  
            fs.Dispose();  
        }  
 
        #endregion  
    }  
}  

转载于:https://www.cnblogs.com/nozer/articles/2301407.html

本模块代码是针对在 2011 年在 CSDN 论坛个发布的“最新 NET 读取纯真IP数据库代码(C#)”源码,做了一次升级,这次升级不是简单的修补,是本人精心的重写,现在只需要 5 分哦,您值得拥有!该源代码不同于网上的代码,网上代码基本可分为两大类,第一类直接使用文件流,通过移动文件流指针(即更改 Stream.Position 属性值)搜索 IP 地址对应的信息,此类代码问题是:其一移动文件指针效率是比较低的(给 Position 赋值),多线程并发时,会重复打开多个文件效率更加底下;第二类是把文件直接加载内存中,通过这种缓冲,速度是提升了,但并没有为多线程环境优化,多线程并发时(如:Web 中每位访客,都是一根线程),意味会重复的读取文件,重复的创建缓存,浪费内存空间。 该源代码特点是考虑到了多线程应用环境(如:Web 每个会话,都是一根线程),设计了缓存对象 QQWryCache 用于管理缓存,用 QQCacheStream 流读取缓存数据。在多线程应用环境中,假设 10 根线程访问同一个纯真 IP 数据库时,只会开辟 1 份缓存,给多根线程共享,避免了不必要的内存浪费。 注1:本模块代码,保证所有静态方法都是线程安全的,但不保证所有实例方法都是线程安全的。 注2:每根线程访问缓存时,请通过 QQWryCache.GetCache 静态方法获取缓存对象。 注3:多根线程获取到的缓存对象,通常都是同一对象,该对象已经考虑了线程同步,不必担心线程安全问题。 /* >>> 使用完全缓存(缓存整个文件,约 8.8MB),调用方法如下: */ QQWryCache cache = QQWryCache.GetCache("qqwry.dat", true); Stream stream = cache.GetCacheStream(); QQWrySearcher searcher = new QQwryScanner(stream); QQWryLocation location = searcher.Query("IP 地址"); Console.WritleLine("Country = {0}, Location = {1}", location.Country, location.Location); /* 完全缓冲, * 缓存一旦初始化完毕,就会自动关闭文件, * 所以不再依赖于文件,因此可以不用关闭缓冲流, * 下面调用 Close 方法,其实没有实际意义,但也不会引发异常。 */ stream.Close(); /* >>> 使用索引缓存(仅缓存索引部分,约 3MB),调用方法如下: <<>> 直接使用文件流(不使用缓存),调用方法如下: <<>> 遍历 IP 数据库。 <<< */ QQWryCache cache = QQWryCache.GetCache("qqwry.dat", true); Stream stream = cache.GetCacheStream(); QQWrySearcher searcher = new QQWrySearcher(stream); // 用 for 循环遍历 for(int i = 0; i < searcher.Count; i++) { QQWryIpLocation item = searcher[i]; Console.WritleLine("Country = {0}, Location = {1}", location.Country, location.Location); } // 用 foreach 循环遍历 foreach(QQWryIpLocation item in searcher) { QQWryIpLocation item = searcher[i]; Console.WritleLine("Country = {0}, Location = {1}", location.Country, location.Location); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值