纯真IP数据库查询模块源代码参考(C#)

发一个自己写的C#的IP所在地查询程序,使用纯真/珊瑚虫数据库均可(QQWry.dat/CoralWry.dat),程序的实际使用价值可能不大,仅作为此类c#程序的参照。

有些朋友在C#中使用动态类对象的构造函数/析构函数在对象的初始化/释放时来打开/关闭数据文件,这虽然是一个很好的面向的思想,但托管程序对象的释放时间是不确定的,尤其是ASP.NET的使用,这一方面由于打开的文件未释放会造成资源浪费,更重要的是可能会带来线程并发所造成的共享冲突,另外如果在ASP.NET中使用缓存机制则会增加程序的复杂性和降低模块的独立性、重用性。

基于这些原因,程序中全部使用静态方法,实际上这是由我的另一个C++的程序改写的C#程序,模块的调用很简单,例如,使用当前目录下的CoralWry.dat数据文件来查询IP地址121.193.213.6,则可以:

  1. IpLocation ipl=IpLocation ipl=IpLocator.GetIpLocation("CoralWry.dat","121.193.213.246");
  2. Console.WriteLine("IP地址段: "+IpLocator.IntToIpString(ipl.IpStart)+" - "+IpLocator.IntToIpString(ipl.IpEnd));
  3. Console.WriteLine("IP所在地: "+ipl.Country+" "+ipl.City);

返回值是一个IpLocation结构,包含4个属性:Country国家、City城市、IpStart/IpEnd IP段开始/结束值(uint型,可使用IntToIpString()函数转成点分IP字符串)

  1. //IpLocator.cs -> IpLocator.dll
  2. using System;
  3. using System.IO;
  4. namespace Hebust
  5. {
  6.     /// <summary>
  7.     /// IpLocator类
  8.     /// </summary>
  9.     public class IpLocator
  10.     {
  11.         // 核心方法:IP搜索
  12.         public static IpLocation GetIpLocation(string fn, string ips)
  13.         {
  14.             if(!File.Exists(fn))
  15.             {
  16.                 throw new Exception("文件不存在!");
  17.             }
  18.             FileStream fs=new FileStream(fn,FileMode.Open,FileAccess.Read,FileShare.Read);
  19.             BinaryReader fp=new BinaryReader(fs);
  20.             //读文件头,获取首末记录偏移量
  21.             int fo=fp.ReadInt32();
  22.             int lo=fp.ReadInt32();
  23.             //IP值
  24.             uint ipv=IpStringToInt(ips);
  25.             // 获取IP索引记录偏移值
  26.             int rcOffset=getIndexOffset(fs,fp,fo,lo,ipv);
  27.             fs.Seek(rcOffset,System.IO.SeekOrigin.Begin);
  28.             IpLocation ipl;
  29.             if(rcOffset>=0)
  30.             {
  31.                 fs.Seek(rcOffset,System.IO.SeekOrigin.Begin);
  32.                 //读取开头IP值
  33.                 ipl.IpStart=fp.ReadUInt32();
  34.                 //转到记录体
  35.                 fs.Seek(ReadInt24(fp),System.IO.SeekOrigin.Begin);
  36.                 //读取结尾IP值
  37.                 ipl.IpEnd=fp.ReadUInt32();
  38.                 ipl.Country=GetString(fs,fp);
  39.                 ipl.City=GetString(fs,fp);
  40.             }
  41.             else
  42.             {
  43.                 //没找到
  44.                 ipl.IpStart=0;
  45.                 ipl.IpEnd=0;
  46.                 ipl.Country="未知国家";
  47.                 ipl.City="未知地址";
  48.             }
  49.             fp.Close();
  50.             fs.Close();
  51.             return ipl;
  52.         }
  53.     
  54.         // 函数功能: 采用“二分法”搜索索引区, 定位IP索引记录位置
  55.         private static int getIndexOffset(FileStream fs, BinaryReader fp, int _fo, int _lo, uint ipv)
  56.         {
  57.             int fo=_fo, lo=_lo;
  58.             int mo;    //中间偏移量
  59.             uint mv;    //中间值
  60.             uint fv,lv; //边界值
  61.             uint llv;   //边界末末值
  62.             fs.Seek(fo,System.IO.SeekOrigin.Begin);
  63.             fv=fp.ReadUInt32();
  64.             fs.Seek(lo,System.IO.SeekOrigin.Begin);
  65.             lv=fp.ReadUInt32();
  66.             //临时作它用,末记录体偏移量
  67.             mo=ReadInt24(fp);
  68.             fs.Seek(mo,System.IO.SeekOrigin.Begin);
  69.             llv=fp.ReadUInt32();
  70.             //边界检测处理
  71.             if(ipv<fv)
  72.                 return -1;
  73.             else if(ipv>llv)
  74.                 return -1;
  75.             //使用"二分法"确定记录偏移量
  76.             do
  77.             {
  78.                 mo=fo+(lo-fo)/7/2*7;
  79.                 fs.Seek(mo,System.IO.SeekOrigin.Begin);
  80.                 mv=fp.ReadUInt32();
  81.                 if(ipv>=mv)
  82.                     fo=mo;
  83.                 else
  84.                     lo=mo;
  85.                 if(lo-fo==7)
  86.                     mo=lo=fo;
  87.             } while(fo!=lo);
  88.             return mo;
  89.         }
  90.         // 字符串数值型判断
  91.         public static bool IsNumeric(string s)
  92.         {
  93.             if(s!=null && System.Text.RegularExpressions.Regex.IsMatch(s, @"^-?/d+$"))
  94.                 return true;
  95.             else
  96.                 return false;
  97.         }
  98.         // IP字符串->长整型值
  99.         public static uint IpStringToInt(string IpString)
  100.         {
  101.             uint Ipv=0;
  102.             string[] IpStringArray=IpString.Split('.');
  103.             int i;
  104.             uint Ipi;
  105.             for(i=0;i<4&&i<IpStringArray.Length;i++)
  106.             {
  107.                 if(IsNumeric(IpStringArray[i]))
  108.                 {
  109.                     Ipi=(uint)Math.Abs(Convert.ToInt32(IpStringArray[i]));
  110.                     if(Ipi>255) Ipi=255;
  111.                     Ipv+=Ipi<<(3-i)*8;
  112.                 }
  113.             }
  114.             return Ipv;
  115.         }
  116.         // 长整型值->IP字符串
  117.         public static string IntToIpString(uint Ipv)
  118.         {
  119.             string IpString="";
  120.             IpString+=(Ipv>>24)+"."+((Ipv&0x00FF0000)>>16)+"."+((Ipv&0x0000FF00)>>8)+"."+(Ipv&0x000000FF);
  121.             return IpString;
  122.         }
  123.         // 读取字符串
  124.         private static string ReadString(BinaryReader fp)
  125.         {
  126.             byte[] TempByteArray=new byte[128];
  127.             int i=0;
  128.             do
  129.             {
  130.                 TempByteArray[i]=fp.ReadByte();
  131.             } while(TempByteArray[i++]!='/0' && i<128);
  132.             return System.Text.Encoding.Default.GetString(TempByteArray).TrimEnd('/0');
  133.         }
  134.         // 读取三字节的整数
  135.         private static int ReadInt24(BinaryReader fp)
  136.         {
  137.             if(fp==nullreturn -1;
  138.             int ret=0;
  139.             ret|=(int)fp.ReadByte();
  140.             ret|=(int)fp.ReadByte()<<8&0xFF00;
  141.             ret|=(int)fp.ReadByte()<<16&0xFF0000;
  142.             return ret;
  143.         }
  144.         // 读取IP所在地字符串
  145.         private static string GetString(FileStream fs, BinaryReader fp)
  146.         {
  147.             byte Tag;
  148.             int Offset;
  149.             Tag=fp.ReadByte();
  150.             if(Tag==0x01)        // 重定向模式1: 城市信息随国家信息定向
  151.             {
  152.                 Offset=ReadInt24(fp);
  153.                 fs.Seek(Offset,System.IO.SeekOrigin.Begin);
  154.                 return GetString(fs,fp);
  155.             }
  156.             else if(Tag==0x02)    // 重定向模式2: 城市信息没有随国家信息定向
  157.             {
  158.                 Offset=ReadInt24(fp);
  159.                 int TmpOffset=(int)fs.Position;
  160.                 fs.Seek(Offset,System.IO.SeekOrigin.Begin);
  161.                 string TmpString=GetString(fs,fp);
  162.                 fs.Seek(TmpOffset,System.IO.SeekOrigin.Begin);
  163.                 return TmpString;
  164.             }
  165.             else    // 无重定向: 最简单模式
  166.             {
  167.                 fs.Seek(-1,System.IO.SeekOrigin.Current);
  168.                 return ReadString(fp);
  169.             }
  170.         }
  171.     }
  172.     // IP查询结果结构
  173.     public struct IpLocation
  174.     {
  175.         public uint IpStart;
  176.         public uint IpEnd;
  177.         public string Country;
  178.         public string City;
  179.     }
  180. }

附C#工程文件(DLL工程,输出IpLocator.dll): http://download.csdn.net/source/610795

 

  (原创文章,转载时请注明本文网址)

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
模块代码是针对在 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); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值