关于QQwry格式
作者 cnss 2004-8-18
版权所有 转载请注明出处
http://blog.csdn.net/cnss
最近写了个比qqwry好的格式,点这里查看.
刚才通过RSS看到一篇关于QQwry格式的blog: http://blog.csdn.net/taft/archive/2004/08/18/77559.aspx
想不到QQwry还在用,这是俺两年前设计的,这个格式该被淘汰了.为什么这么说呢,因为它采用的是索引+二分查找来减小内存占用和提高查找速度.
由于采用二分查找,所以IP数据要被分为最小的片,假设有A,B两条数据,B数据完全覆盖A数据,那么转换为QQwry后两条数据就变成了三条.如果原始数据非常有条理,就可以避免这个现象,不过这是不可能的,几万条数据会越来越乱,所以QQwry的尺寸会迅速增加,之所以增长的不是特别快,是因为格式对重复数据有一定压缩.
QQwry.dat:"咦?我没吃那么多,怎么胖的那么快!?"
那篇文章是作者猜测的格式,我再把原来整理的发一遍吧.0x2 0x0 0x0 0x0不是错误,可能是给御风而行放版权信息的地方,另外附带ipsearcher.dll源码.
-----------------------------------------------
新格式说明
主要分为数据区和索引区
★数据区元素:
存放IP信息中的:结束IP(4字节),国家(不定长),地区(不定长)
排列顺序:无要求
★索引区元素:
存放IP信息中的:起始IP(4字节),索引值(3字节)
排列顺序:起始IP按升序排列
★IP为4字节,如"255.0.0.0"表示为0xFF000000,存在文件中则为00 00 00 FF(字节序原因)
★索引值为该IP消息的<结束IP、国家、地区>在文件中的位置。指向<结束IP>
★如果结束IP后的字节为0x01,则说明该IP消息的<国家、地区>与前面的IP信息重复,这时0x01后面的3个字节为国家、地区字符串的偏移量。可以根据这三个字节去前面找国家、地区。
★如果国家的第一个字节为0x02,说明该国家串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
★如果地区的第一个字节为0x02,说明该地区串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
★有可能在出现0x01的情况下出现0x02,这时需要跳转两次查找国家、地区字符串。
★正常的字符串以NULL做结尾。
★IP信息不允许有重复、覆盖
★使用索引是为了保证能以线性速度搜索
★新格式不允许为未知数据的IP消息,原格式中的未知数据已经都去掉了。如果有未知数据的IP信息,将大大增加文件长度。
文件的头4个字节是索引区第一个元素的偏移量,第二个4字节是索引区最后一个元素的偏移量。通过这两个偏移量,可以用二分法快速查找IP信息。如:一条IP信息是,要查询的IP为150
起始 结束 国家 地区
100 200 中国 北京
首先在索引区找到起始IP小于150的最后一个元素,通过索引,找到结束IP,如果150大于结束IP,说明是未知数据;如果150小于等于结束IP,则找到国家、地区。
// ipsearcher.cpp : Defines the entry point for the DLL application. // #include "stdafx.h" #include <windows.h> #include <stdlib.h> extern "C" __declspec(dllexport) void* __cdecl _GetAddress(const char *IPstr); void *ret[2]; //for return char *ptr = NULL; //ptr of image char *p = NULL; //point to index unsigned int total; //ip count inline unsigned int get_3b(const char *mem) { return 0x00ffffff & *(unsigned int*)(mem); } inline void Load(void) { HANDLE hnd; //file handle DWORD NumberOfBytesRead; //len char text[2048]; //patch char *temp; unsigned int len; //get patch if( !GetModuleFileName(0, text, 2048) ) return; temp = strrchr(text, 92); // 92 = '/' *(temp + 1) = NULL; strcat(temp, "QQwry.dat"); //CreateFile hnd = CreateFile(text, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(INVALID_HANDLE_VALUE == hnd) { ::MessageBox(NULL, text, "不能打开文件!", NULL); return; } //get len len = SetFilePointer(hnd, NULL, NULL, FILE_END); SetFilePointer(hnd, NULL, NULL, FILE_BEGIN); //malloc ptr = (char*)malloc(len+9); if(!ptr) { CloseHandle(hnd); ::MessageBox(NULL, "不能分配内存!", NULL, NULL); return; } //read if(!ReadFile(hnd, ptr, len, &NumberOfBytesRead, NULL)) { CloseHandle(hnd); free(ptr); ::MessageBox(NULL, text, "不能读入文件!", NULL); return; } CloseHandle(hnd); //calc total - 1 total = (*((unsigned int*)ptr+1) - *(unsigned int*)ptr); //check file if(total % 7 != 0) { free(ptr); ::MessageBox(NULL, text, "QQwry.dat文件有损坏!", NULL); return; } total /= 7; ++total; p = ptr + *(unsigned int*)ptr; //ptr of index area } inline unsigned int str2ip(const char *lp) { unsigned int ret = 0; unsigned int now = 0; while(*lp) { if('.' == *lp) { ret = 256 * ret + now; now = 0; } else now = 10 * now + *lp - '0'; ++lp; } ret = 256 * ret + now; return ret; } void* __cdecl _GetAddress(const char *IPstr) { if(NULL == p) { ret[0] = "无法打开数据"; ret[1] = ""; return ret; } unsigned int ip = str2ip(IPstr); char *now_p; unsigned int begin = 0, end = total; while(1) { if( begin >= end - 1 ) break; if( ip < *(unsigned int*)(p + (begin + end)/2 * 7) ) end = (begin + end)/2; else begin = (begin + end)/2; } unsigned int temp = get_3b(p + 7 * begin + 4); if(ip <= *(unsigned int*)(ptr + temp)) //ok, found { now_p = ptr + temp + 4; if( 0x01 == *now_p ) now_p = ptr + get_3b(now_p + 1); //country if( 0x02 == *now_p ) //jump { ret[0] = ptr + get_3b(now_p + 1); now_p += 4; } else { ret[0] = now_p; for(; *now_p; ++now_p) ; ++now_p; } //local if( 0x02 == *now_p ) //jump ret[1] = ptr + get_3b(now_p + 1); else ret[1] = now_p; } else { ret[0] = "未知数据"; ret[1] = ""; } return ret; } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch(ul_reason_for_call) { //case DLL_THREAD_ATTACH: //case DLL_THREAD_DETACH: case DLL_PROCESS_ATTACH: //attach { Load(); } break; //------------------------------------ case DLL_PROCESS_DETACH: //detach { free(ptr); } } return true; }
发表于 @ 2004年08月18日 02:40:00 | 评论( 19 ) | 举报| 收藏
页面右面有地址啊
新的格式文件中有的,02 xx xx xx 不一定是便宜量
这是为什么?
通过文件的头8个字节就可以得出索引区在哪儿结束,后面的就是这些附加信息.当初只有几个版本是这样的.
当mode为1时省略mode也可以.
CString NL_QQwry::GetCountryLocal(BYTE bMode,int ioffset)
{
CString buf="";
if(bMode==1)//X 没有跳
{buf=GetStr(ioffset);buf+=" ";buf+=GetStr();}
if(bMode==2)//X Country不跳 Local 跳
{buf=GetStr(ioffset);buf+=" ";buf+=GetStr(m_ei.offset1);}
if(bMode==3)//2 Country跳 local不跳
{buf=GetStr(m_ei.offset1);buf+=" ";buf+=GetStr(ioffset+4);}
if(bMode==4)//2 Country跳 local跳
{buf=GetStr(m_ei.offset1);buf+=" ";buf+=GetStr(m_ei.offset2);}
if(bMode==5)//1 没有跳
{buf=GetStr(m_ei.offset1);buf+=" ";buf+=GetStr();}
if(bMode==6)//1 Country不跳 Local 跳
{buf=GetStr(m_ei.offset1);buf+=" ";buf+=GetStr(m_ei.offset2);}
if(bMode==7)//1 Country跳 Local 不跳
{buf=GetStr(m_ei.offset2);buf+=" ";buf+=GetStr(m_ei.offset1+4); }
if(bMode==8)//1 Country跳 Local跳
{buf=GetStr(m_ei.offset1);buf+=" ";buf+=GetStr(m_ei.offset2);}
return buf;
}
其实收集IP的人更辛苦,他们每天花好几个小时在这上面,一坚持就是几年,其中的枯燥是我们不能想象的.
更应该感谢他们.
从此基础上编写了NL_QQwry Class,之后转换成php格式
http://www.jiyongs.com/ftp/upload/QQwry.rar
DD D1 1D 00 22 CC 2D 00 FF FF FF 00 49 41 4E
其中FF FF FF 00是IP,49 41 4E是索引值么?
似乎不是,索引区和数据区是混杂在一起的?这样索引查找会快么?
我不是很懂,请大家给我讲讲,谢谢。
DD D1 1D 00 22 CC 2D 00 FF FF FF 00 49 41 4E
其中FF FF FF 00是IP,49 41 4E是索引值么?
似乎不是,索引区和数据区是混杂在一起的?这样索引查找会快么?
我不是很懂,请大家给我讲讲,谢谢。
在IPSearcher中有,分配空间时为何加9??
//malloc
ptr = (char*)malloc(nLength + 9);//为何加9 ??
if(!ptr)
{
CloseHandle(hFile);
MessageBox(NULL, "不能分配内存!", NULL, NULL);
return;
}