使用C#读取QQ纯真数据库

按照LumaQQ介绍的该数据库的格式,

http://lumaqq.linuxsir.org/article/qqwry_format_detail.html

我用C#写了一个查找函数,还真折腾了一阵,不过还好,总算明白这个模式一和模式二的意思了。把思路好好整理了一下,下面的代码的可读性应该比较好了,用List<>的地方是VS2005以后的,要改成VS2003下也比较容易用ArrayList,不过这个效率真的是@$#$##@,呵呵,谁叫.Net有那么严格的类型安全呢。^_^

主要代码如下,基本思想也是先定位到索引段,然后在索引段查找IP记录的位偏移,最后把该IP记录找出来,这个dll可以很方便的部署到Web上或者WinForm中:

 

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

namespace QQIPSeekLib
{
    public class RedirectMode
    {
        public static readonly int Mode_1 = 1;
        public static readonly int Mode_2 = 2;
    }

    public class IPFormat
    {
        public static readonly int HeaderLength = 8;
        public static readonly int IndexRecLength = 7;
        public static readonly int IndexOffset = 3;
        public static readonly int RecOffsetLength = 3;

        public static readonly string UnknownCountry = "未知的国家";
        public static readonly string UnknownZone = "未知的地区";

        public static uint ToUint(byte[] val)
        {
            if (val.Length > 4) throw new ArgumentException();
            if (val.Length < 4)
            {
                byte[] copyBytes = new byte[4];
                Array.Copy(val, 0, copyBytes, 0, val.Length);
                return BitConverter.ToUInt32(copyBytes, 0);
            }
            else
            {
                return BitConverter.ToUInt32(val, 0);
            }
        }
    }

    public class IPLocation
    {
        private IPAddress m_ip;
        private string m_country;
        private string m_loc;

        public IPLocation(IPAddress ip, string country, string loc)
        {
            m_ip = ip;
            m_country = country;
            m_loc = loc;
        }

        public IPAddress IP
        {
            get { return m_ip; }
        }

        public string Country
        {
            get { return m_country; }
        }

        public string Zone
        {
            get { return m_loc; }
        }
    }

    /// <summary>
    /// This class used to control ip seek
    /// </summary>
    public class IPSeeker
    {
        private string m_libPath;
        private uint m_indexStart;
        private uint m_indexEnd;
        public IPSeeker(string libPath)
        {
            m_libPath = libPath;
            //Locate the index block
            using (FileStream fs = new FileStream(m_libPath, FileMode.Open, FileAccess.Read))
            {

                BinaryReader reader = new BinaryReader(fs);
                Byte[] header = reader.ReadBytes(IPFormat.HeaderLength);
                m_indexStart = BitConverter.ToUInt32(header, 0);
                m_indexEnd = BitConverter.ToUInt32(header, 4);

            }
        }

        /// <summary>
        /// 输入IP地址,获取IP所在的地区信息
        /// </summary>
        /// <param name="ip">待查询的IP地址</param>
        /// <returns></returns>
        public IPLocation GetLocation(IPAddress ip)
        {
            using (FileStream fs = new FileStream(m_libPath, FileMode.Open, FileAccess.Read))
            {
                BinaryReader reader = new BinaryReader(fs);
                //Because it is network order(BigEndian), so we need to transform it into LittleEndian
                Byte[] givenIpBytes = BitConverter.GetBytes(IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ip.GetAddressBytes(), 0)));
                uint offset = FindStartPos(fs, reader, m_indexStart, m_indexEnd, givenIpBytes);
                return GetIPInfo(fs, reader, offset, ip, givenIpBytes);
            }
        }

        #region private method
       
        private uint FindStartPos(FileStream fs, BinaryReader reader, uint m_indexStart, uint m_indexEnd, byte[] givenIp)
        {
            uint givenVal = BitConverter.ToUInt32(givenIp, 0);
            fs.Position = m_indexStart;

            while (fs.Position <= m_indexEnd)
            {
                Byte[] bytes = reader.ReadBytes(IPFormat.IndexRecLength);
                uint curVal = BitConverter.ToUInt32(bytes, 0);
                if (curVal > givenVal)
                {
                    fs.Position = fs.Position - 2 * IPFormat.IndexRecLength;
                    bytes = reader.ReadBytes(IPFormat.IndexRecLength);
                    byte[] offsetByte = new byte[4];
                    Array.Copy(bytes, 4, offsetByte, 0, 3);
                    return BitConverter.ToUInt32(offsetByte, 0);
                }
            }
            return 0;
        }

        private IPLocation GetIPInfo(FileStream fs, BinaryReader reader, long offset, IPAddress ipToLoc, Byte[] ipBytes)
        {
            fs.Position = offset;
            //To confirm that the given ip is within the range of record IP range
            byte[] endIP = reader.ReadBytes(4);
           
            uint endIpVal = BitConverter.ToUInt32(endIP, 0);
            uint ipVal = BitConverter.ToUInt32(ipBytes, 0);
            if (endIpVal < ipVal) return null;

            string country;
            string zone;
            //Read the Redirection pattern byte
            Byte pattern = reader.ReadByte();
           
            if (pattern == RedirectMode.Mode_1)
            {
                Byte[] countryOffsetBytes = reader.ReadBytes(IPFormat.RecOffsetLength);
                uint countryOffset = IPFormat.ToUint(countryOffsetBytes);

                if (countryOffset == 0) return GetUnknownLocation(ipToLoc);

                fs.Position = countryOffset;
                if (fs.ReadByte() == RedirectMode.Mode_2)
                {
                    return ReadMode2Record(fs, reader, ipToLoc);
                }
                else
                {
                    fs.Position--;
                    country = ReadString(reader);
                    zone = ReadZone(fs, reader, Convert.ToUInt32(fs.Position));
                }
            }
            else if (pattern == RedirectMode.Mode_2)
            {
                return ReadMode2Record(fs, reader, ipToLoc);
            }
            else
            {
                fs.Position--;
                country = ReadString(reader);
                zone = ReadZone(fs, reader, Convert.ToUInt32(fs.Position));
            }
            return new IPLocation(ipToLoc, country, zone);

        }

        //When it is in Mode 2
        private IPLocation ReadMode2Record(FileStream fs, BinaryReader reader, IPAddress ip)
        {
            uint countryOffset = IPFormat.ToUint(reader.ReadBytes(IPFormat.RecOffsetLength));
            uint curOffset = Convert.ToUInt32(fs.Position);
            if (countryOffset == 0) return GetUnknownLocation(ip);
            fs.Position = countryOffset;
            string country = ReadString(reader);
            string zone = ReadZone(fs, reader, curOffset);
            return new IPLocation(ip, country, zone);
        }

        //return a Unknown Location
        private IPLocation GetUnknownLocation(IPAddress ip)
        {
            string country = IPFormat.UnknownCountry;
            string zone = IPFormat.UnknownZone;
            return new IPLocation(ip, country, zone);
        }
       
        //Retrieve the zone info
        private string ReadZone(FileStream fs, BinaryReader reader, uint offset)
        {
            fs.Position = offset;
            byte b = reader.ReadByte();
            if (b == RedirectMode.Mode_1 || b == RedirectMode.Mode_2)
            {
                uint zoneOffset = IPFormat.ToUint(reader.ReadBytes(3));
                if (zoneOffset == 0) return IPFormat.UnknownZone;
                return ReadZone(fs, reader, zoneOffset);
            }
            else
            {
                fs.Position--;
                return ReadString(reader);
            }
        }

        private string ReadString(BinaryReader reader)
        {
            List<byte> stringLst = new List<byte>();
            byte byteRead = 0;
            while ((byteRead = reader.ReadByte()) != 0)
            {
                stringLst.Add(byteRead);
            }
            return Encoding.GetEncoding("gb2312").GetString(stringLst.ToArray());
        }
       
        #endregion
    }

   
}

 

 

随文把测试程序也一并送上:

 

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

namespace  TestQQLib
{
    
class  Program
    {
        
static   void  Main( string [] args)
        {
            
while  ( true )
            {
                Console.WriteLine(
" 请输入IP地址(如:192.168.0.1),直接按回车结束: " );
                
string  ip  =  Console.ReadLine();
                
if  (ip  ==   string .Empty)  return ;
                IPSeeker seeker 
=   new  IPSeeker( " QQWry.Dat " );
                IPAddress ipaddr 
=  IPAddress.Parse(ip);
                IPLocation loc 
=  seeker.GetLocation(ipaddr);
                
if  (loc  ==   null )
                {
                    Console.WriteLine(
" 指定的IP地址无效! " );
                }
                Console.WriteLine(
" 地址: "   +  loc.Country  +  loc.Zone);
            }
            
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值