zoukankan      html  css  js  c++  java
  • QQwry.dat 数据结构 存储结构 解析[C#代码]

    存储格式: A.文件头(大小8字节), B.记录区(不定长),C.索引区(大小由文件头决定)
    A.文件头是8个字节长度,前四个字节存储了第一条索引的绝对地址,
       后四个字节存储了最后一条索引的绝对地址。
      (2^32字节=4GB)
     
    B.记录的格式是[IP地址][国家记录][地区记录]: ip:4字节, 国家记录(字符串,0结尾), 地区记录(字符串,0结尾)
        
      [字段形式]
         国家名或者地区名,我们就有了两个可能:
         第一就是直接的字符串表示的国家名,
         第二就是一个4字节的结构, 第一个字节表明了重定向的模式, 后面3个字节是国家名或者地区名的实际偏移位置(指针)。
      [重定向的模式]有两种:0x01、0x02,
        0x01:表示指针指向完整的信息;
        0x02:表示指针指向国家或地区其中一个字段信息.
      [实际的case]:
         a:0x01XXXXXX->[国家][地区]
         b:(0x02XXXXXX->[国家])[地区]
            [国家](0x02XXXXXX->[地区])
         c: 0x01XXXXXX->(0x02XXXXXX->[国家])[地区]   
            0x01XXXXXX->[国家](0x02XXXXXX->[地区])
            0x01XXXXXX->(0x02XXXXXX->[国家])(02XXXXXX->[地区])        
         
      [版权信息]    
        实际上就是最后一条IP记录,最后一条记录显示出来就是这样:
        255.255.255.0 255.255.255.255 纯真网络 2004年6月25日IP数据。  
    C.索引结构. 条索引长度为7个字节,前4个字节是起始IP地址,
     后三个字节就指向了IP记录。
     那么有没有结束IP? 假设有这么一条记录:166.111.0.0 - 166.111.255.255,
     那么166.111.0.0就是起始IP,166.111.255.255就是结束IP,
     结束IP就是IP记录中的那头4个字节,
     

    IPUtil
     1 public class IPLocation
     2 {
     3     public String country;
     4     public String area;
     5 
     6     public IPLocation()
     7     {
     8         country = area = "";
     9     } 
    10 }
    11 
    12 public class IPUtil
    13 {
    14 
    15     public static string GetIpArea(string filepath, string ip)
    16     {
    17         if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath))
    18             return string.Empty;
    19         IPWry Instance=new IPWry(filepath);
    20         if (string.IsNullOrEmpty(ip))
    21             return string.Empty;
    22         else if (ip == "0.0.0.0")
    23             return "(未获得IP)";
    24 
    25         else if (ip == "127.0.0.1")
    26             return "服务器";
    27 
    28         else if (ip.StartsWith("192.168.") || ip.StartsWith("10."))
    29             return "本地网络";
    30         else if (ip == "::1")
    31             return "服务器";
    32 
    33         else if (IsIPv6(ip))
    34             return "(不支持ipv6)";
    35 
    36         try
    37         {
    38             IPLocation location =Instance.SearchIPLocation(ip);
    39             if (location != null)
    40                 return location.country + location.area;
    41         }
    42         catch
    43         {
    44             return "(未知地址)";
    45         }
    46 
    47         return string.Empty;
    48     }
    49 
    50 
    51     public static bool IsIPv6(string ip)
    52     {
    53         int flag = ip.IndexOf(':');
    54         if (flag > 0 && flag < ip.LastIndexOf(':'))
    55         {
    56             return true;
    57         }
    58         return false;
    59     }
    60 
    61 
    62 
    63 
    64 }
    bbsmax.IPWry
      1 public class IPWry
      2 {
      3 //文件头: 读取索引的开始位置和结束位置
      4 private long ipBegin,ipEnd;
      5 
      6 //记录区 重定向的两种模式   
      7 private const byte REDIRECT_MODE_1 = 0x01;       
      8 private const byte REDIRECT_MODE_2 = 0x02;
      9 
     10 //索引区: 字节长度为7  其中[4B(IP)+3B(记录区地址)]     
     11 private const int IP_RECORD_LENGTH = 7;     
     12      
     13 
     14 //数据库文件      
     15 private FileStream ipFile;
     16 private const string unCountry = "未知国家";
     17 private const string unArea = "未知地区"; 
     18 
     19 //存储文本内容 100字节缓冲区      
     20 private byte[] buf;
     21 
     22 //存储3个字节     
     23 private byte[] b3;
     24 
     25 //存储4个字节       
     26 private byte[] b4;
     27 
     28 //IP地址对象       
     29 private IPLocation loc;
     30 
     31 //ipfile:IP数据库文件绝对路径
     32 public IPWry(string ipfile) 
     33 {
     34 
     35     buf = new byte[100];
     36     b3 = new byte[3];
     37     b4 = new byte[4];
     38     try
     39     {
     40         ipFile = new FileStream(ipfile, FileMode.Open, FileAccess.Read, FileShare.Read);
     41 
     42     }
     43     catch
     44     {
     45         throw new ArgumentNullException("打开IP数据库文件出错!");
     46     }
     47 
     48     ipBegin = readLong4(0);
     49     ipEnd = readLong4(4);
     50     loc = new IPLocation();
     51 }     
     52      
     53      
     54 public IPLocation SearchIPLocation(string ip)
     55 {           
     56 
     57     //将字符IP转换为字节
     58     string[] ipSp = ip.Split('.');
     59     if (ipSp.Length != 4)
     60     {
     61         throw new ArgumentOutOfRangeException("不是合法的IP地址!");
     62     }
     63     byte[] IP = new byte[4];
     64     for (int i = 0; i < IP.Length; i++)
     65     {
     66         IP[i] = (byte)(Int32.Parse(ipSp[i]) & 0xFF);
     67     }
     68 
     69     IPLocation local = null;
     70     long offset = locateIP(IP);//获取
     71 
     72     if (offset != -1)
     73     {
     74         local = getIPLocation(offset);
     75     }
     76 
     77     if (local == null)
     78     {
     79         local = new IPLocation();
     80         local.area = unArea;
     81         local.country = unCountry;
     82     }
     83     return local;
     84 }
     85         
     86 
     87 //取得具体信息        
     88 private IPLocation getIPLocation(long offset)
     89 {
     90     ipFile.Position = offset + 4;
     91     //读取第一个字节判断是否是标志字节
     92     byte one = (byte)ipFile.ReadByte();
     93     if (one == REDIRECT_MODE_1)
     94     {
     95         //第一种模式
     96         //读取国家偏移
     97         long countryOffset = readLong3();
     98         //转至偏移处
     99         ipFile.Position = countryOffset;
    100         //再次检查标志字节
    101         byte b = (byte)ipFile.ReadByte();
    102         if (b == REDIRECT_MODE_2)
    103         {
    104             loc.country = readString(readLong3());
    105             ipFile.Position = countryOffset + 4;
    106         }
    107         else
    108             loc.country = readString(countryOffset);
    109 
    110         //读取地区标志
    111         loc.area = readArea(ipFile.Position);
    112 
    113     }
    114     else if (one == REDIRECT_MODE_2)
    115     {
    116         //第二种模式
    117         loc.country = readString(readLong3());
    118         loc.area = readArea(offset + 8);
    119     }
    120     else
    121     {
    122         //普通模式
    123         loc.country = readString(--ipFile.Position);
    124         loc.area = readString(ipFile.Position);
    125     }
    126     return loc;
    127 }
    128 
    129 
    130 //取得地区信息    
    131 private string readArea(long offset)
    132 {
    133     ipFile.Position = offset;
    134     byte one = (byte)ipFile.ReadByte();
    135     if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2)
    136     {
    137         long areaOffset = readLong3(offset + 1);
    138         if (areaOffset == 0)
    139             return unArea;
    140         else
    141         {
    142             return readString(areaOffset);
    143         }
    144     }
    145     else
    146     {
    147         return readString(offset);
    148     }
    149 }
    150 
    151 
    152 //读取字符串      
    153 private string readString(long offset)
    154 {
    155     ipFile.Position = offset;
    156     int i = 0;     
    157 
    158     do
    159     {
    160         buf[i] = (byte)ipFile.ReadByte();           
    161     } while (buf[i++] != (byte)(0));
    162 
    163     i--;
    164     if (i > 0)
    165         return Encoding.Default.GetString(buf, 0, i);
    166     else
    167         return "";
    168 }
    169 
    170 
    171 //查找IP地址所在的绝对偏移量      
    172 private long locateIP(byte[] ip)
    173 {
    174     long m = 0;
    175     int r;
    176 
    177     //比较第一个IP项
    178     readIP(ipBegin, b4);
    179     r = compareIP(ip, b4);
    180     if (r == 0)
    181         return ipBegin;
    182     else if (r < 0)
    183         return -1;
    184     //开始二分搜索
    185     for (long i = ipBegin, j = ipEnd; i < j; )
    186     {
    187         m = this.getMiddleOffset(i, j);
    188         readIP(m, b4);
    189         r = compareIP(ip, b4);
    190         if (r > 0)
    191             i = m;
    192         else if (r < 0)
    193         {
    194             if (m == j)
    195             {
    196                 j -= IP_RECORD_LENGTH;
    197                 m = j;
    198             }
    199             else
    200             {
    201                 j = m;
    202             }
    203         }
    204         else
    205             return readLong3(m + 4);
    206     }
    207     m = readLong3(m + 4);
    208     readIP(m, b4);
    209     r = compareIP(ip, b4);
    210     if (r <= 0)
    211         return m;
    212     else
    213         return -1;
    214 }
    215 
    216 //取得begin和end之间的偏移量  用于二分搜索
    217 private long getMiddleOffset(long begin, long end)
    218 {
    219     long records = (end - begin) / IP_RECORD_LENGTH;
    220     records >>= 1;
    221     if (records == 0)
    222         records = 1;
    223     return begin + records * IP_RECORD_LENGTH;
    224 }
    225 
    226 
    227 //读出4字节的IP地址  调换字节顺序
    228 private void readIP(long offset, byte[] ip)
    229 {
    230     ipFile.Position = offset;
    231     ipFile.Read(ip, 0, ip.Length);
    232     byte tmp = ip[0];
    233     ip[0] = ip[3];
    234     ip[3] = tmp;
    235     tmp = ip[1];
    236     ip[1] = ip[2];
    237     ip[2] = tmp;
    238 }
    239 
    240 
    241 //比较IP地址是否相同    
    242 private int compareIP(byte[] ip, byte[] beginIP)
    243 {
    244     for (int i = 0; i < 4; i++)
    245     {
    246         int r = compareByte(ip[i], beginIP[i]);
    247         if (r != 0)
    248             return r;
    249     }
    250     return 0;
    251 }
    252 
    253 
    254 //比较两个字节是否相等  
    255 private int compareByte(byte bsrc, byte bdst)
    256 {
    257     if ((bsrc & 0xFF) > (bdst & 0xFF))
    258         return 1;
    259     else if ((bsrc ^ bdst) == 0)
    260         return 0;
    261     else
    262         return -1;
    263 }
    264 
    265 //字节序为little-endian即低位在低地址
    266 //从绝对位置 读取4字节     
    267 private long readLong4(long offset)
    268 {
    269     long ret = 0;
    270     ipFile.Position = offset;
    271     ret |= (ipFile.ReadByte() & 0xFF);
    272     ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
    273     ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
    274     ret |= ((ipFile.ReadByte() << 24) & 0xFF000000);
    275     return ret;
    276 }
    277 
    278 
    279 //从绝对位置 读取3字节      
    280 private long readLong3(long offset)
    281 {
    282     long ret = 0;
    283     ipFile.Position = offset;
    284     ret |= (ipFile.ReadByte() & 0xFF);
    285     ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
    286     ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
    287     return ret;
    288 }
    289 
    290 //从当前位置读取3字节  
    291 private long readLong3()
    292 {         
    293     long ret = 0;     
    294     ret |= (ipFile.ReadByte() & 0xFF);
    295     ret |= ((ipFile.ReadByte() << 8) & 0xFF00);
    296     ret |= ((ipFile.ReadByte() << 16) & 0xFF0000);
    297     return ret;
    298 }
    299       
    300 }


    [注意]QQWry.dat里面全部采用了little-endian字节序
    [参考]http://www.jb51.net/article/17197.htm

  • 相关阅读:
    Blazor server VS Blazor WebAssembly
    DataTable 差集Except、交集Intersect、并集Union
    Power BI Anomaly Detection
    Power BI智能叙述
    Configure the Secure Store Service in SharePoint Server
    小批量excel数据导入oracle
    python批量写入oracle
    centos7安装java,配置的环境变量不生效 -bash: java: command not found
    nebula docker三件套组件版本问题
    shell删除指定行
  • 原文地址:https://www.cnblogs.com/AspDotNetMVC/p/2764610.html
Copyright © 2011-2022 走看看