C#根据IP查询省份、城市,纯真IP库实现方式+无IP库实现方式

首先,要获取到一个IP地址,可参照上文:http://blog.youkuaiyun.com/konw_nothing/article/details/8888526


第一种方式:

读取纯真IP库的封装类:

要下载数据文件的话,直接百度搜“纯真ip数据库”,一般就能找到了。文件名通常是 qqwry.dat

using System;
using System.IO;
using System.Text;

/// <summary>
/// IP对象类,该对象包含所属国家和地区
/// </summary>
public class IPLocation
{
    public string ISP { get; set; }
    public string Area { get; set; }
}

/// <summary>
/// 读取QQ纯真IP数据库 
/// </summary>    
public class QQWry
{
    //第一种模式 
    private const byte REDIRECT_MODE_1 = 0x01;

    //第二种模式 
    private const byte REDIRECT_MODE_2 = 0x02;

    //每条记录长度 
    private const int IP_RECORD_LENGTH = 7;

    //数据库文件 
    private FileStream ipFile = null;

    private const string unISP = "未知ISP";
    private const string unArea = "未知地区";

    //索引开始位置 
    private long ipBegin;

    //索引结束位置 
    private long ipEnd;

    //IP地址对象 
    private IPLocation loc;

    //存储文本内容 
    private byte[] buf;

    //存储3字节 
    private byte[] b3;

    //存储4字节 
    private byte[] b4;

    /// <summary> 
    /// 构造函数 
    /// </summary> 
    /// <param name="ipfile">IP数据库文件绝对路径</param> 
    /// <returns></returns> 
    public QQWry(string ipfile)
    {
        buf = new byte[100];
        b3 = new byte[3];
        b4 = new byte[4];
        lock (this)
        {
            ipFile = new FileStream(ipfile, FileMode.Open);
            ipBegin = ReadLong4(0);
            ipEnd = ReadLong4(4);
            loc = new IPLocation();
        }
    }

    /// <summary> 
    /// 搜索IP地址搜索 
    /// </summary> 
    /// <param name="ip"></param> 
    /// <returns></returns> 
    public IPLocation SearchIPLocation(string ip)
    {
        //将字符IP转换为字节 
        string[] ipSp = ip.Split('.');
        if (ipSp.Length != 4)
        {
            ip = "127.0.0.1";
            ipSp = ip.Split('.');           
        }
        byte[] IP = new byte[4];
        for (int i = 0; i < IP.Length; i++)
        {
            IP[i] = (byte)(Int32.Parse(ipSp[i]) & 0xFF);
        }

        IPLocation local = null;
        long offset = LocateIP(IP);

        if (offset != -1)
        {
            local = getIPLocation(offset);
        }

        if (local == null)
        {
            local = new IPLocation();
            local.Area = unArea;
            local.ISP = unISP;
        }
        return local;
    }

    /// <summary> 
    /// 取得具体信息 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <returns></returns> 
    private IPLocation getIPLocation(long offset)
    {
        ipFile.Position = offset + 4;
        //读取第一个字节判断是否是标志字节 
        byte one = (byte)ipFile.ReadByte();
        if (one == REDIRECT_MODE_1)
        {
            //第一种模式 
            //读取国家偏移 
            long countryOffset = ReadLong3();
            //转至偏移处 
            ipFile.Position = countryOffset;
            //再次检查标志字节 
            byte b = (byte)ipFile.ReadByte();
            if (b == REDIRECT_MODE_2)
            {
                loc.Area = ReadString(ReadLong3());
                ipFile.Position = countryOffset + 4;
            }
            else
                loc.Area = ReadString(countryOffset);

            //读取运营商标志
            loc.ISP = ReadArea(ipFile.Position);

        }
        else if (one == REDIRECT_MODE_2)
        {
            //第二种模式 
            loc.Area = ReadString(ReadLong3());
            loc.ISP = ReadArea(offset + 8);
        }
        else
        {
            //普通模式 
            loc.Area = ReadString(--ipFile.Position);
            loc.ISP = ReadString(ipFile.Position);
        }
        ipFile.Close();
        return loc;
    }

    /// <summary> 
    /// 读取地区名称 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <returns></returns> 
    private string ReadArea(long offset)
    {
        ipFile.Position = offset;
        byte one = (byte)ipFile.ReadByte();
        if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2)
        {
            long areaOffset = ReadLong3(offset + 1);
            return (areaOffset == 0) ? unArea : ReadString(areaOffset);
        }
        else
        {
            return ReadString(offset);
        }
    }

    /// <summary> 
    /// 读取字符串 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <returns></returns> 
    private string ReadString(long offset)
    {
        ipFile.Position = offset;
        int i = 0;
        for (i = 0, buf[i] = (byte)ipFile.ReadByte(); buf[i] != (byte)(0); buf[++i] = (byte)ipFile.ReadByte()) ;
        return (i > 0) ? Encoding.Default.GetString(buf, 0, i) : "";
    }

    /// <summary> 
    /// 查找IP地址所在的绝对偏移量 
    /// </summary> 
    /// <param name="ip"></param> 
    /// <returns></returns> 
    private long LocateIP(byte[] ip)
    {
        long m = 0;
        int r;

        //比较第一个IP项 
        ReadIP(ipBegin, b4);
        r = CompareIP(ip, b4);
        if (r == 0)
            return ipBegin;
        else if (r < 0)
            return -1;
        //开始二分搜索 
        for (long i = ipBegin, j = ipEnd; i < j; )
        {
            m = this.GetMiddleOffset(i, j);
            ReadIP(m, b4);
            r = CompareIP(ip, b4);
            if (r > 0)
                i = m;
            else if (r < 0)
            {
                if (m == j)
                {
                    m = j -= IP_RECORD_LENGTH;
                }
                else
                {
                    j = m;
                }
            }
            else
                return ReadLong3(m + 4);
        }
        m = ReadLong3(m + 4);
        ReadIP(m, b4);
        return (CompareIP(ip, b4) <= 0) ? m : -1;
    }

    /// <summary> 
    /// 从当前位置读取四字节,此四字节是IP地址 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <param name="ip"></param> 
    private void ReadIP(long offset, byte[] ip)
    {
        ipFile.Position = offset;
        ipFile.Read(ip, 0, ip.Length);
        byte tmp = ip[0];
        ip[0] = ip[3];
        ip[3] = tmp;
        tmp = ip[1];
        ip[1] = ip[2];
        ip[2] = tmp;
    }

    /// <summary> 
    /// 比较IP地址是否相同 
    /// </summary> 
    /// <param name="ip"></param> 
    /// <param name="beginIP"></param> 
    /// <returns>0:相等,1:ip大于beginIP,-1:小于</returns> 
    private int CompareIP(byte[] ip, byte[] beginIP)
    {
        for (int i = 0; i < 4; i++)
        {
            int r = CompareByte(ip[i], beginIP[i]);
            if (r != 0)
                return r;
        }
        return 0;
    }

    /// <summary> 
    /// 比较两个字节是否相等 
    /// </summary> 
    /// <param name="bsrc"></param> 
    /// <param name="bdst"></param> 
    /// <returns></returns> 
    private int CompareByte(byte bsrc, byte bdst)
    {
        if ((bsrc & 0xFF) > (bdst & 0xFF))
            return 1;
        else if ((bsrc ^ bdst) == 0)
            return 0;
        else
            return -1;
    }

    /// <summary> 
    /// 从当前位置读取4字节,转换为长整型 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <returns></returns> 
    private long ReadLong4(long offset)
    {
        long ret = 0;
        ipFile.Position = offset;
        ret |= (ipFile.ReadByte() & 0xFF);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
        ret |= ((ipFile.ReadByte() << 24) & 0xFF000000);

        return ret;
    }

    /// <summary> 
    /// 根据当前位置,读取3字节 
    /// </summary> 
    /// <param name="offset"></param> 
    /// <returns></returns> 
    private long ReadLong3(long offset)
    {
        long ret = 0;
        ipFile.Position = offset;
        ret |= (ipFile.ReadByte() & 0xFF);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
        return ret;
    }

    /// <summary> 
    /// 从当前位置读取3字节 
    /// </summary> 
    /// <returns></returns> 
    private long ReadLong3()
    {
        long ret = 0;
        ret |= (ipFile.ReadByte() & 0xFF);
        ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
        ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
        return ret;
    }

    /// <summary> 
    /// 取得begin和end中间的偏移 
    /// </summary> 
    /// <param name="begin"></param> 
    /// <param name="end"></param> 
    /// <returns></returns> 
    private 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;
    }
}

使用方式:

IPLocation ip = new QQWry(Server.MapPath("~/App_Data/QQWry.Dat")).SearchIPLocation("58.83.255.255");
Response.Write("ISP:" + ip.ISP + "<br/>");//铁通
Response.Write("Area:" + ip.Area);//北京市朝阳区

第二种方式:

使用第三方接口

介绍一种新浪的接口:其他接口大同小异

接口地址:http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip={ip}

public class IpDetail
{
    public string Ret { get; set; }
    public string Start { get; set; }
    public string End { get; set; }
    public string Country { get; set; }
    public string Province { get; set; }
    public string City { get; set; }
    public string District { get; set; }
    public string Isp { get; set; }
    public string Type { get; set; }
    public string Desc { get; set; }
    public static IpDetail Get(string ipAddress, Encoding sourceEncoding)
    {
        string ip = string.Empty;
        if (sourceEncoding == null)
            sourceEncoding = Encoding.UTF8;
        using (var receiveStream = System.Net.WebRequest.Create(string.Format("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip={0}", ipAddress)).GetResponse().GetResponseStream())
        {
            using (var sr = new StreamReader(receiveStream, sourceEncoding))
            {
                ip = sr.ReadToEnd();
            }
        }
        return !string.IsNullOrEmpty(ip) ? new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<IpDetail>(ip) : null;
    }
}

使用方式:

IpDetail d = IpDetail.Get("58.83.255.255", null);
Response.Write(d.Country + "<br/>");
Response.Write(d.Province + "<br/>");
Response.Write(d.City + "<br/>");
Response.Write(d.Isp);


另外,可以结合两种方式,当纯真库查不出数据时,有可能是纯真库没有及时更新,那么就能用接口方式来获取,一般第三方的IP库还是有保证的。


本模块代码是针对在 2011 年在 优快云 论坛个发布的“最新 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、付费专栏及课程。

余额充值