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

 

 

你可能感兴趣的:(数据结构)