QQwry.dat 数据结构 存储结构 解析[C#代码]
2012-11-11 13:10
579 查看
存储格式: 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个字节,
bbsmax.IPWry
[注意]QQWry.dat里面全部采用了little-endian字节序
[参考]http://www.jb51.net/article/17197.htm
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个字节,
bbsmax.IPWry
public class IPWry { //文件头: 读取索引的开始位置和结束位置 private long ipBegin,ipEnd; //记录区 重定向的两种模式 private const byte REDIRECT_MODE_1 = 0x01; private const byte REDIRECT_MODE_2 = 0x02; //索引区: 字节长度为7 其中[4B(IP)+3B(记录区地址)] private const int IP_RECORD_LENGTH = 7; //数据库文件 private FileStream ipFile; private const string unCountry = "未知国家"; private const string unArea = "未知地区"; //存储文本内容 100字节缓冲区 private byte[] buf; //存储3个字节 private byte[] b3; //存储4个字节 private byte[] b4; //IP地址对象 private IPLocation loc; //ipfile:IP数据库文件绝对路径 public IPWry(string ipfile) { buf = new byte[100]; b3 = new byte[3]; b4 = new byte[4]; try { ipFile = new FileStream(ipfile, FileMode.Open, FileAccess.Read, FileShare.Read); } catch { throw new ArgumentNullException("打开IP数据库文件出错!"); } ipBegin = readLong4(0); ipEnd = readLong4(4); loc = new IPLocation(); } public IPLocation SearchIPLocation(string ip) { //将字符IP转换为字节 string[] ipSp = ip.Split('.'); if (ipSp.Length != 4) { throw new ArgumentOutOfRangeException("不是合法的IP地址!"); } 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.country = unCountry; } return local; } //取得具体信息 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.country = readString(readLong3()); ipFile.Position = countryOffset + 4; } else loc.country = readString(countryOffset); //读取地区标志 loc.area = readArea(ipFile.Position); } else if (one == REDIRECT_MODE_2) { //第二种模式 loc.country = readString(readLong3()); loc.area = readArea(offset + 8); } else { //普通模式 loc.country = readString(--ipFile.Position); loc.area = readString(ipFile.Position); } return loc; } //取得地区信息 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); if (areaOffset == 0) return unArea; else { return readString(areaOffset); } } else { return readString(offset); } } //读取字符串 private string readString(long offset) { ipFile.Position = offset; int i = 0; do { buf[i] = (byte)ipFile.ReadByte(); } while (buf[i++] != (byte)(0)); i--; if (i > 0) return Encoding.Default.GetString(buf, 0, i); else return ""; } //查找IP地址所在的绝对偏移量 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) { j -= IP_RECORD_LENGTH; m = j; } else { j = m; } } else return readLong3(m + 4); } m = readLong3(m + 4); readIP(m, b4); r = compareIP(ip, b4); if (r <= 0) return m; else return -1; } //取得begin和end之间的偏移量 用于二分搜索 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; } //读出4字节的IP地址 调换字节顺序 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; } //比较IP地址是否相同 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; } //比较两个字节是否相等 private int compareByte(byte bsrc, byte bdst) { if ((bsrc & 0xFF) > (bdst & 0xFF)) return 1; else if ((bsrc ^ bdst) == 0) return 0; else return -1; } //字节序为little-endian即低位在低地址 //从绝对位置 读取4字节 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; } //从绝对位置 读取3字节 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; } //从当前位置读取3字节 private long readLong3() { long ret = 0; ret |= (ipFile.ReadByte() & 0xFF); ret |= ((ipFile.ReadByte() << 8) & 0xFF00); ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); return ret; } }
[注意]QQWry.dat里面全部采用了little-endian字节序
[参考]http://www.jb51.net/article/17197.htm
相关文章推荐
- C#读取QQ纯真IP数据库QQWry.Dat的代码
- C# IFF图形结构解析代码
- [数据结构]程杰队列的链式存储结构及实现代码
- C# 一个自己写的树结构代码(2)-Array,HashTable,List,String数据结构操作封装
- 【Java数据结构学习笔记之一】线性表的存储结构及其代码实现
- C#如何不使用递归实现无限层次结构的代码分享[转]
- 数据结构之图(存储结构、遍历)
- C# 解析 json Newtonsoft果然强大,代码写的真好
- C#基础解析之Ⅲ 【循环结构】
- 看数据结构写代码(22) 二叉树的顺序存储方式
- hashmap存储结构解析
- C#创建安全的字典(Dictionary)存储结构
- 字符串的动态存储结构的代码实现
- C#创建安全的栈(Stack)存储结构
- 用最简洁有效的代码执行存储过程 C#
- java编程无向图结构的存储及DFS操作代码详解
- 按Sybase的PowerDesigner工具设计的数据库模型 ---&gt; 解析生成能兼容多种数据库的相应的C#底层代码
- docker存储结构解析
- qqwry.dat的数据结构图文解释第1/2页
- C#获取执行存储过程的" 返回值"代码