存储格式: 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

 

 

posted on 2012-11-11 13:10  极简  阅读(921)  评论(0编辑  收藏  举报