基本结构
QQWry.dat文件在结构上分为3块:文件头,记录区,索引区。一般我们要查找IP时,先在索引区查找记录偏移,然后再到记录区读出信息。由于记录区的记录是不定长的,所以直接在记录区中搜索是不可能的。由于记录数比较多,如果我们遍历索引区也会是有点慢的,一般来说,我们可以用二分查找法搜索索引区,其速度比遍历索引区快若干数量级。图1是QQWry.dat的文件结构图。
图1. QQWry.dat文件结构
要注意的是,QQWry.dat里面全部采用了little-endian字节序
一. 了解文件头
QQWry.dat的文件头只有8个字节,其结构非常简单,首四个字节是第一条索引的绝对偏移,后四个字节是最后一条索引的绝对偏移。
二. 了解记录区
每条IP记录都由国家和地区名组成,国家地区在这里并不是太确切,因为可能会查出来”清华大学计算机系”之类的,这里清华大学就成了国家名了,所以这个国家地区名和IP数据库制作的时候有关系。所以记录的格式有点像QName,有一个全局部分和局部部分组成,我们这里还是沿用国家名和地区名的说法。
于是我们想象着一条记录的格式应该是: [IP地址][国家名][地区名],当然,这个没有什么问题,但是这只是最简单的情况。很显然,国家名和地区名可能会有很多的重复,如果每条记录都保存一个完整的名称拷贝是非常不理想的,所以我们就需要重定向以节省空间。所以为了得到一个国家名或者地区名,我们就有了两个可能:第一就是直接的字符串表示的国家名,第二就是一个4字节的结构,第一个字节表明了重定向的模式,后面3个字节是国家名或者地区名的实际偏移位置。对于国家名来说,情况还可能更复杂些,因为这样的重定向最多可能有两次。
那么什么是重定向模式?根据上面所说,一条记录的格式是[IP地址][国家记录][地区记录],如果国家记录是重定向的话,那么地区记录是有可能没有的,于是就有了两种情况,我管他叫做模式1和模式2。我们对这些格式的情况举图说明:
图2. IP记录的最简单形式
图2表示了最简单的IP记录格式,我想没有什么可以解释的
图3. 重定向模式1
图3演示了重定向模式1的情况。我们看到在模式1的情况下,地区记录也跟着国家记录走了,在IP地址之后只剩下了国家记录的4字节,后面3个字节构成了一个指针,指向了实际的国家名,然后又跟着地址名。模式1的标识字节是0×01。
图4. 重定向模式2
图4演示了重定向模式2的情况。我们看到了在模式2的情况下(其标识字节是0×02),地区记录没有跟着国家记录走,因此在国家记录之后4个字节之后还是有地区记录。我想你已经明白了模式1和模式2的区别,即:模式1的国家记录后面不会再有地区记录,模式2的国家记录后会有地区记录。下面我们来看一下更复杂的情况。
图5. 混和情况1
图5演示了当国家记录为模式1的时候可能出现的更复杂情况,在这种情况下,重定向指向的位置仍然是个重定向,不过第二次重定向为模式2。大家不用担心,没有模式3了,这个重定向也最多只有两次,并且如果发生了第二次重定向,则其一定为模式2,而且这种情况只会发生在国家记录上,对于地区记录,模式1和模式2是一样的,地区记录也不会发生2次重定向。不过,这个图还可以更复杂,如图7:
图6. 混和情况2
图6是模式1下最复杂的混和情况,不过我想应该也很好理解,只不过地区记录也来重定向而已,有一点我要提醒你,如果重定向的地址是0,则表示未知的地区名。
所以我们总结如下:一条IP记录由[IP地址][国家记录][地区记录]组成,对于国家记录,可以有三种表示方式:字符串形式,重定向模式1和重定向模式2。对于地区记录,可以有两种表示方式:字符串形式和重定向,另外有一条规则:重定向模式1的国家记录后不能跟地区记录。按照这个总结,在这些方式中合理组合,就构成了IP记录的所有可能情况。
设计的理由
在我们继续去了解索引区的结构之前,我们先来了解一下为何记录区的结构要如此设计。我想你可能想到了答案:字符串重用。没错,在这种结构下,对于一个国家名和地区名,我只需要保存其一次就可以了。我们举例说明,为了表示方便,我们用小写字母代表IP记录,C表示国家名,A表示地区名:
- 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2, A1 = A2,那么我们就可以使用图3显示的结构来实现重用
- 有三条记录a(C1, A1), b(C2, A2), c(C3, A3),如果C1 = C2, A2 = A3,现在我们想存储记录b,那么我们可以用图6的结构来实现重用
- 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2,现在我们想存储记录b,那么我们可以采用模式2表示C2,用字符串表示A2
你可以举出更多的情况,你也会发现在这种结构下,不同的字符串只需要存储一次。
了解索引区
在”了解文件头”部分,我们说明了文件头实际上是两个指针,分别指向了第一条索引和最后一条索引的绝对偏移。如图8所示:
图8. 文件头指向索引区图示
实在是很简单,不是吗?从文件头你就可以定位到索引区,然后你就可以开始搜索IP了!每条索引长度为7个字节,前4个字节是起始IP地址,后三个字节就指向了IP记录。这里有些概念需要说明一下,什么是起始IP,那么有没有结束IP? 假设有这么一条记录:166.111.0.0 – 166.111.255.255,那么166.111.0.0就是起始IP,166.111.255.255就是结束IP,结束IP就是IP记录中的那头4个字节,这下你应该就清楚了吧。于是乎,每条索引配合一条记录,构成了一个IP范围,如果你要查找166.111.138.138所在的位置,你就会发现166.111.138.138落在了166.111.0.0 – 166.111.255.255 这个范围内,那么你就可以顺着这条索引去读取国家和地区名了。那么我们给出一个最详细的图解吧:
图9. 文件详细结构
现在一切都清楚了是不是?也许还有一点你不清楚,QQWry.dat的版本信息存在哪里呢? 答案是:最后一条IP记录实际上就是版本信息,最后一条记录显示出来就是这样:255.255.255.0 255.255.255.255 纯真网络 2004年6月25日IP数据。OK,到现在你应该全部清楚了。
首先我们来获取这两个偏移值:
#include <stdio.h> #define DAT_FILE "../ipdat/qqwry.dat" void parseDat(FILE *fp); int main(void) { FILE *fp = NULL; fp = fopen(DAT_FILE, "rb"); if (fp == NULL ) { printf("open file error!\n"); return 0; } parseDat(fp); fclose(fp); return 0; } void parseDat(FILE *fp) { unsigned int startIndexOffset = 0; unsigned int endIndexOffset = 0; fseek(fp, 0L, SEEK_SET); fread(&startIndexOffset, 4, 1, fp); fread(&endIndexOffset, 4, 1, fp); printf("%u ~ %u", startIndexOffset, endIndexOffset); }结果:
6240699 ~ 9348825
#include <stdio.h> #define DAT_FILE "../ipdat/qqwry.dat" void parseDat(FILE *fp); int main(void) { FILE *fp = NULL; fp = fopen(DAT_FILE, "rb"); if (fp == NULL ) { printf("open file error!\n"); return 0; } parseDat(fp); fclose(fp); return 0; } void parseDat(FILE *fp) { long int startIndexOffset = 0; long int endIndexOffset = 0; long int indexOffset = 0; long int recordOffset = 0; unsigned int startIp = 0; unsigned int endIp = 0; int i; fseek(fp, 0L, SEEK_SET); fread(&startIndexOffset, 4, 1, fp); fread(&endIndexOffset, 4, 1, fp); indexOffset = startIndexOffset; for (i = 0; i < 16; i++) { fseek(fp, indexOffset, SEEK_SET); fread(&startIp, 4, 1, fp); fread(&recordOffset, 3, 1, fp); indexOffset = ftell(fp); printf("%u.%u.%u.%u ~ ", (startIp >> 24) & 0xff, (startIp >> 16) & 0xff, (startIp >> 8) & 0xff, startIp & 0xff); fseek(fp, recordOffset, SEEK_SET); fread(&endIp, 4, 1, fp); printf("%u.%u.%u.%u\n", (endIp >> 24) & 0xff, (endIp >> 16) & 0xff, (endIp >> 8) & 0xff, endIp & 0xff); } }结果:
0.0.0.0 ~ 0.255.255.255 1.0.0.0 ~ 1.0.0.255 1.0.1.0 ~ 1.0.3.255 1.0.4.0 ~ 1.0.7.255 1.0.8.0 ~ 1.0.15.255 1.0.16.0 ~ 1.0.31.255 1.0.32.0 ~ 1.0.63.255 1.0.64.0 ~ 1.0.127.255 1.0.128.0 ~ 1.0.255.255 1.1.0.0 ~ 1.1.0.255 1.1.1.0 ~ 1.1.1.255 1.1.2.0 ~ 1.1.7.255 1.1.8.0 ~ 1.1.63.255 1.1.64.0 ~ 1.1.127.255 1.1.128.0 ~ 1.1.255.255 1.2.0.0 ~ 1.2.1.255将所有数据另存在txt文本文件中:
#include <stdio.h> #define DAT_FILE "../ipdat/qqwry.dat" #define OUT_FILE "../ipdat/out.txt" FILE *out = NULL; void showIp(unsigned int ip) { char buffer[20] = {0}; sprintf(buffer, "%u.%u.%u.%u ", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); fputs(buffer, out); } void getString(FILE *fp) //讀取字符串并写入文件,以0为结尾标志 { char temp[128] = {0}; int index = 0; while ((temp[index] = fgetc(fp)) != 0) { index++; } fputs(temp, out); } void getInfo(FILE *fp, int flag) //获取数据区字符串信息,flag为1时表示已经读了一半字符串 { unsigned char mod; long int offset = 0; long int preOffset = 0; mod = fgetc(fp); //获取模式 if (mod > 2) //如果无重定向 { fseek(fp, -1L, SEEK_CUR); //返回 getString(fp); //读取一个字符串 if (flag == 0) //如果调用函数时还未读 { getInfo(fp, 1); //标志置1,递归调用 } return; } fread(&offset, 3, 1, fp); //如果有重定向,获取重定向偏移 if (mod == 1) //如果模式一 { fseek(fp, offset, SEEK_SET); //seek到目标地址 getInfo(fp, flag); //递归读 return; } preOffset = ftell(fp); //如果模式二,记住当前偏移 fseek(fp, offset, SEEK_SET); //寻目标地址 getInfo(fp, 1); //读一个字符串 if (flag == 0) //如果调用函数时还未读 { fseek(fp, preOffset, SEEK_SET); //还要回到原来位置 getInfo(fp, 1); //再读一次 } } void parseRecord(FILE *fp, long int recordOffset) { unsigned int endIp = 0; fseek(fp, recordOffset, SEEK_SET); //seek到数据区 fread(&endIp, 4, 1, fp); //读取结束IP showIp(endIp); //显示结束IP getInfo(fp, 0); //获取信息 fputs("\n", out); } void parseDat(FILE *fp) { long int startIndexOffset = 0; long int endIndexOffset = 0; long int indexOffset = 0; long int recordOffset = 0; unsigned int startIp = 0; fseek(fp, 0L, SEEK_SET); //seek文件头 fread(&startIndexOffset, 4, 1, fp); //读索引开始偏移量 fread(&endIndexOffset, 4, 1, fp); //读索引结束偏移量 indexOffset = startIndexOffset; while (indexOffset <= endIndexOffset) //循环从根据索引读取数据 { fseek(fp, indexOffset, SEEK_SET); //seek到索引位置 fread(&startIp, 4, 1, fp); //读取索引中的开始IP fread(&recordOffset, 3, 1, fp); //读取对应数据区的偏移量 indexOffset = ftell(fp); //记住索引的偏移量 showIp(startIp); //显示开始IP parseRecord(fp, recordOffset); //读取数据区 } } int main(void) { FILE *fp = NULL; out = fopen(OUT_FILE, "wb"); fp = fopen(DAT_FILE, "rb"); if (fp == NULL ) { printf("open file error!\n"); return 0; } parseDat(fp); fclose(fp); return 0; }结果:
223.255.232.0 223.255.235.255 澳大利亚 北领地Territory Technology Solutions公司 223.255.236.0 223.255.239.255 上海市 康宝莱(中国)保健品有限公司 223.255.240.0 223.255.243.255 香港 五邑發展有限公司 223.255.244.0 223.255.247.255 印度 CZ88.NET 223.255.248.0 223.255.251.255 澳大利亚 新南威尔士州Viocorp国际控股有限公司 223.255.252.0 223.255.253.255 福建省 电信 223.255.254.0 223.255.254.255 新加坡 滨海湾金沙私人有限公司 223.255.255.0 223.255.255.255 澳大利亚 CZ88.NET 224.0.0.0 224.255.255.255 IANA CZ88.NET 225.0.0.0 239.255.255.255 IANA保留地址 用于多点传送 240.0.0.0 247.255.255.255 IANA保留地址 CZ88.NET 248.0.0.0 248.255.255.255 IANA保留地址 CZ88.NET 249.0.0.0 254.255.255.255 IANA保留地址 CZ88.NET 255.0.0.0 255.255.254.255 CZ88.NET 255.255.255.0 255.255.255.255 纯真网络 2013年4月20日IP数据能读到最后一条。
#include <stdio.h> #define DAT_FILE "../ipdat/qqwry.dat" #define OUT_FILE "../ipdat/out.sql" FILE *out = NULL; void showIp(unsigned int ip) { char buffer[20] = {0}; sprintf(buffer, "\"%u.%u.%u.%u\"", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); fputs(buffer, out); } void getString(FILE *fp) //讀取字符串并写入文件,以0为结尾标志 { char temp[128] = {0}; int index = 0; while ((temp[index] = fgetc(fp)) != 0) { index++; } fputs(temp, out); } void getInfo(FILE *fp, int flag) //获取数据区字符串信息,flag为1时表示已经读了一半字符串 { unsigned char mod; long int offset = 0; long int preOffset = 0; mod = fgetc(fp); //获取模式 if (mod > 2) //如果无重定向 { fseek(fp, -1L, SEEK_CUR); //返回 getString(fp); //读取一个字符串 if (flag == 0) //如果调用函数时还未读 { getInfo(fp, 1); //标志置1,递归调用 } return; } fread(&offset, 3, 1, fp); //如果有重定向,获取重定向偏移 if (mod == 1) //如果模式一 { fseek(fp, offset, SEEK_SET); //seek到目标地址 getInfo(fp, flag); //递归读 return; } preOffset = ftell(fp); //如果模式二,记住当前偏移 fseek(fp, offset, SEEK_SET); //寻目标地址 getInfo(fp, 1); //读一个字符串 if (flag == 0) //如果调用函数时还未读 { fseek(fp, preOffset, SEEK_SET); //还要回到原来位置 getInfo(fp, 1); //再读一次 } } void parseRecord(FILE *fp, long int recordOffset) { unsigned int endIp = 0; fseek(fp, recordOffset, SEEK_SET); //seek到数据区 fread(&endIp, 4, 1, fp); //读取结束IP fputs(",", out); showIp(endIp); //显示结束IP fputs(",\"", out); getInfo(fp, 0); //获取信息 fputs("\");\n", out); } void parseDat(FILE *fp) { long int startIndexOffset = 0; long int endIndexOffset = 0; long int indexOffset = 0; long int recordOffset = 0; unsigned int startIp = 0; fputs("CREATE TABLE qqwry(start_ip varchar(16), end_ip varchar(16), address varchar(32));\n", out); fseek(fp, 0L, SEEK_SET); //seek文件头 fread(&startIndexOffset, 4, 1, fp); //读索引开始偏移量 fread(&endIndexOffset, 4, 1, fp); //读索引结束偏移量 indexOffset = startIndexOffset; while (indexOffset <= endIndexOffset) //循环从根据索引读取数据 { fseek(fp, indexOffset, SEEK_SET); //seek到索引位置 fread(&startIp, 4, 1, fp); //读取索引中的开始IP fread(&recordOffset, 3, 1, fp); //读取对应数据区的偏移量 indexOffset = ftell(fp); //记住索引的偏移量 fputs("INSERT INTO qqwry VALUES(", out); showIp(startIp); //显示开始IP parseRecord(fp, recordOffset); //读取数据区 } } int main(void) { FILE *fp = NULL; out = fopen(OUT_FILE, "wb"); fp = fopen(DAT_FILE, "rb"); if (fp == NULL ) { printf("open file error!\n"); return 0; } parseDat(fp); fclose(fp); return 0; }生成的SQL语句:
CREATE TABLE qqwry(start_ip varchar(16), end_ip varchar(16), address varchar(32)); INSERT INTO qqwry VALUES("0.0.0.0","0.255.255.255","IANA保留地址 CZ88.NET"); INSERT INTO qqwry VALUES("1.0.0.0","1.0.0.255","澳大利亚 CZ88.NET"); INSERT INTO qqwry VALUES("1.0.1.0","1.0.3.255","福建省电信"); INSERT INTO qqwry VALUES("1.0.4.0","1.0.7.255","澳大利亚 CZ88.NET"); INSERT INTO qqwry VALUES("1.0.8.0","1.0.15.255","广东省电信"); INSERT INTO qqwry VALUES("1.0.16.0","1.0.31.255","日本Beacon服务器"); INSERT INTO qqwry VALUES("1.0.32.0","1.0.63.255","广东省电信"); INSERT INTO qqwry VALUES("1.0.64.0","1.0.127.255","日本 CZ88.NET"); INSERT INTO qqwry VALUES("1.0.128.0","1.0.255.255","泰国 CZ88.NET"); INSERT INTO qqwry VALUES("1.1.0.0","1.1.0.255","福建省电信"); INSERT INTO qqwry VALUES("1.1.1.0","1.1.1.255","澳大利亚 CZ88.NET"); INSERT INTO qqwry VALUES("1.1.2.0","1.1.7.255","福建省电信"); INSERT INTO qqwry VALUES("1.1.8.0","1.1.63.255","广东省电信"); INSERT INTO qqwry VALUES("1.1.64.0","1.1.127.255","日本 CZ88.NET"); INSERT INTO qqwry VALUES("1.1.128.0","1.1.255.255","泰国 CZ88.NET"); INSERT INTO qqwry VALUES("1.2.0.0","1.2.1.255","福建省电信"); INSERT INTO qqwry VALUES("1.2.2.0","1.2.2.255","北京市海淀区北龙中网(北京)科技有限责任公司");
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <inttypes.h> #include "qqwry.h" int main(void) { char ip[32] = {0}; char country[1024]={0}; char area[1024]={0}; FILE *wry_file; wry_file = fopen("../ipdat/qqwry.dat","rb"); gets(ip); qqwry_get_location(country,area,ip,wry_file); fclose(wry_file); if (strlen(country)>0) { printf("%s",country); } if (strlen(area)>0) { if (strlen(country)>0) { printf(" "); } if (strlen(country)<=0) { printf("unknown"); } else { printf("%s",area); } } printf("\n"); return 0; }QQwry.c
/* * Copyright 2008,2009 Surf Chen <http://www.surfchen.org> * * * This source code is under the terms of the * GNU Lesser General Public License. * see <http://www.gnu.org/licenses/lgpl.txt> */ #include <stdio.h> #include <string.h> #include <inttypes.h> #define BE_32(x) ((((uint8_t*)(x))[0]<<24) |\ (((uint8_t*)(x))[1]<<16) |\ (((uint8_t*)(x))[2]<<8) |\ ((uint8_t*)(x))[3]) #define LE_32(x) ((((uint8_t*)(x))[3]<<24) |\ (((uint8_t*)(x))[2]<<16) |\ (((uint8_t*)(x))[1]<<8) |\ ((uint8_t*)(x))[0]) #define LE_24(x) ((((uint8_t*)(x))[2]<<16) |\ (((uint8_t*)(x))[1]<<8) |\ ((uint8_t*)(x))[0]) #define REDIRECT_TYPE_1 0x01 #define REDIRECT_TYPE_2 0x02 static uint32_t ip2long(const char *ip) { uint32_t ip_long=0; uint8_t ip_len=strlen(ip); uint32_t ip_sec=0; int8_t ip_level=3; uint8_t i,n; for (i=0;i<=ip_len;i++) { if (i!=ip_len && ip[i]!='.' && ip[i]<48 || ip[i]>57) { continue; } if (ip[i]=='.' || i==ip_len) { /*too many .*/ if (ip_level==-1) { return 0; } for (n=0;n<ip_level;n++) { ip_sec*=256; } ip_long+=ip_sec; if (i==ip_len) { break; } ip_level--; ip_sec=0; } else { /*char '0' == int 48*/ ip_sec=ip_sec*10+(ip[i]-48); } } return ip_long; } static uint32_t search_index(const uint32_t ip,FILE *qqwry_file) { uint32_t index_ip; unsigned char head[8]; unsigned char index_bytes[7]; fread(head,8,1,qqwry_file); uint32_t index_start,index_end,index_mid; index_start = (uint32_t)LE_32(&head[0]); index_end = (uint32_t)LE_32(&head[4]); while (1) { if ((index_end-index_start)==7) { break; } //printf("index:%u:%u\n",index_start,index_end); index_mid=index_end/7 - index_start/7; if (index_mid%2==0) { index_mid=index_mid/2; } else { index_mid=(index_mid+1)/2; } index_mid=index_start+index_mid*7; fseek(qqwry_file,index_mid,SEEK_SET); fread(index_bytes,7,1,qqwry_file); index_ip=(uint32_t)LE_32(&index_bytes[0]); if (index_ip==ip) { break; } else if (index_ip<ip) { index_start=index_mid; } else { index_end=index_mid; } } if (index_ip>ip) { fseek(qqwry_file,index_start,SEEK_SET); fread(index_bytes,7,1,qqwry_file); } return (uint32_t)LE_24(&index_bytes[4]); } static int readOrJumpRead(char *location,FILE *qqwry_file,const uint32_t data_index) { unsigned char c; unsigned char data_index_bytes[3]; uint32_t jump_data_index=0; if (data_index) { fseek(qqwry_file,data_index,SEEK_SET); } c=fgetc(qqwry_file); switch (c) { case REDIRECT_TYPE_2: case REDIRECT_TYPE_1: fread(data_index_bytes,3,1,qqwry_file); jump_data_index=LE_24(&data_index_bytes[0]); fseek(qqwry_file,jump_data_index,SEEK_SET); break; default: location[strlen(location)]=c; } if (c) { while (c=fgetc(qqwry_file)) { location[strlen(location)]=c; } } return 1; } static int is_cz88(const char *str) { int i; int l=strlen(str)-7; for (i=0;i<l;i++) { if (str[i]=='C' && str[i+1]=='Z' && str[i+2]=='8' && str[i+3]=='8' && str[i+4]=='.' && str[i+5]=='N' && str[i+6]=='E' && str[i+7]=='T' ) { return 1; } } return 0; } int qqwry_get_location_by_long(char *addr1,char *addr2,const uint32_t ip,FILE *qqwry_file) { //printf("%u",ip); unsigned char data_index_bytes[3]; uint32_t data_index; uint32_t addr2_offset; unsigned char c; if (!qqwry_file) { return 0; } fseek(qqwry_file,0,SEEK_SET); data_index = search_index(ip,qqwry_file); //fprintf(stderr,"index:%u:%u\n",ftell(qqwry_file),data_index); /*ip 4 + mode byte 1*/ fseek(qqwry_file,data_index+4,SEEK_SET); c=fgetc(qqwry_file); if (c==REDIRECT_TYPE_1) { fread(data_index_bytes,3,1,qqwry_file); data_index=LE_24(&data_index_bytes[0]); fseek(qqwry_file,data_index,SEEK_SET); c=fgetc(qqwry_file); /*制造一个假的4bytes位移,抵充ip*/ data_index-=4; } if (c==REDIRECT_TYPE_2) { /* * ip 4 + mode byte 1 + addr1 offset 3 * 这里ip的4个bytes不一定是真的,有可能是上一条注释里提到的情况 */ addr2_offset=data_index+8; fread(data_index_bytes,3,1,qqwry_file); data_index=LE_24(&data_index_bytes[0]); fseek(qqwry_file,data_index,SEEK_SET); while (c=fgetc(qqwry_file)) { addr1[strlen(addr1)]=c; } readOrJumpRead(addr2,qqwry_file,addr2_offset); } else { addr1[strlen(addr1)]=c; while (c=fgetc(qqwry_file)) { addr1[strlen(addr1)]=c; } readOrJumpRead(addr2,qqwry_file,0); } if (is_cz88(addr1)) { addr1[0]='\0'; } if (is_cz88(addr2)) { addr2[0]='\0'; } return 1; } int qqwry_get_location(char *addr1,char *addr2,const char *ip,FILE *qqwry_file) { return qqwry_get_location_by_long(addr1,addr2,ip2long(ip),qqwry_file); }
/* * Copyright 2008,2009 Surf Chen <http://www.surfchen.org> * * * This source code is under the terms of the * GNU Lesser General Public License. * see <http://www.gnu.org/licenses/lgpl.txt> */ #include <inttypes.h> /* * 请自己给addr1和addr2分配内存,我的建议是:addr1为64字节,addr2为128字节。 * 这个库不会自己分配内存,而是把结果写入addr1和addr2的内存。 * 这样的设计可以使得调用方更方便地使用自己的内存机制。 * * addr1是大的范围,例如南宁市 * addr2是小的范围,例如邕宁区 * * get_location和get_location_by_long的区别是前者传递的ip是一个字符串,例如 * 222.89.22.122,这个也是我们通常所用的格式;后者传递的ip是一个十进制的ipv4地址 */ int qqwry_get_location(char *addr1,char *addr2,const char *ip,FILE *qqwry_file); int qqwry_get_location_by_long(char *addr1,char *addr2,const uint32_t ip,FILE *qqwry_file);
相关推荐
qqwry.dat, 纯真IP地址数据库镜像,mirror of qqwry.dat.zip
GeoIP.dat、qqhostinfo.pm、qqwry.pl和QQWry.dat3是与网络地理定位和网站统计分析相关的文件。这些文件在IT领域中扮演着重要的角色,特别是在服务器管理和网络安全监控方面。以下是对这些文件及其用途的详细解释: ...
然后,我们需要编写函数来读取qqwry.dat文件,解析IP地址记录,以及根据给定的IP地址找到匹配的记录。 1. 文件读取:首先,使用fopen函数打开qqwry.dat文件,确保以二进制模式("rb")打开,因为数据是以二进制格式...
qqwry.dat qqwry 纯真IP数据库文件,dat数据非exe安装包,每天会扫描两次(17:55和23:55),当发现更新时也可以选择手动运行来获得文件。 目录说明 1、qqwry.dat 为最新数据直链(即下即用),每次更新会覆盖之前的...
QQWry.dat是一个知名的IP地址库,用于快速查询和定位全球IP地址的地理位置信息。这个压缩包文件"QQWry.dat.zip"包含了QQWry.dat这个数据库文件,它被广泛应用于各种软件和网络服务中,以帮助用户识别和追踪互联网上...
qqwry.dat 纯真ip数据库 附在线升级工具 下载下来直接安装即可,ip数据库(qqwry.dat)在安装所在目录下
QQWry.Dat文件通常是以二进制格式存储的,因此我们需要编写一个函数来正确解析这个文件。这个过程包括读取二进制数据、转换为可读的字符格式,然后解析这些数据以获取IP地址和地理位置信息。以下是一个简单的解析...
由于QQWry.Dat文件通常是以特定的二进制格式存储,因此需要对文件结构有深入理解,并编写代码来解析这个格式。这涉及到文件I/O操作、数据结构和算法的知识。 在项目中,开发者可能已经创建了一个IP定位服务类,该类...
要使用QQwry.dat进行IP定位,你需要编写或使用已有的解析程序。这些程序通常用C++、Python、Java等编程语言实现,它们会读取dat文件,根据二分查找算法执行查询操作。在处理过程中,需要注意IP地址的字节序问题,...
2. 源码会有一个类或者方法用于解析QQWRY.DAT,这个过程通常包括解析IP地址块和对应地理位置的结构,找到特定IP地址所在的记录。 3. 在`testip.java`中,可能会有一个main方法,作为程序的入口点,接收用户输入的IP...
在Java编程中,读取纯真IP数据库QQwry.dat是为了实现快速地查询IP地址对应的城市和地区信息。QQwry.dat是一种广泛使用的IP地址库,它包含了大量的IP地址及其地理位置信息,通常用于网络日志分析、网站访问统计、地理...
这个可执行文件可能实现了快速查询和解析qqwry.dat中的IP数据的功能,为用户提供一个直观的界面来输入IP地址并查看其地理位置信息。这样的工具简化了IP查询的过程,使得非技术用户也能轻松操作。 `readme.txt`通常...
qqwry.dat纯真IP 2023年9月更新 2023年最新版 相对于上个版本最新更新超过了100000个IP信息
PHP中实现这个功能,通常会有一个解析qqwry.dat文件的函数。例如,`ipCity.php`可能就是这样一个脚本,它包含了读取和解析数据库文件的逻辑。这个函数可能会使用二进制搜索算法,因为qqwry.dat文件通常是二进制格式...
QQWry.Dat是一个常用的IP地址数据库,它主要用于将IP地址转换为对应的地理位置信息,比如国家、地区等。这个数据库通常被各种网络应用和软件用来显示访客的来源地,或者进行网络管理与分析。在提供的压缩包中,...
QQWry.dat是由“纯真IP数据库”提供的IP地址解析文件,主要用于将IPv4地址转换为地理位置信息。这个数据库由热心网友维护,定期更新,包含了大量全球IP地址的对应信息,尤其是中国地区的IP数据。由于其准确性和实用...
首先,我们来解析一下"qqwry.dat"。这并非一份普通的文本文件,而是一种经过压缩和编码的数据结构,用于存储全球范围内的IP地址及其对应的地理位置信息。每一个IP地址都与一个国家、地区或城市相对应,这种对应关系...
1. 数据解析:首先,我们需要理解QQWRY.DAT文件的内部结构。这个文件通常是二进制格式,包含一系列IP地址和它们对应的地理位置信息。解析这个文件需要编写特定的程序或使用已有工具,将二进制数据解码为可读的文本...
本文将详细讨论如何使用QQwry.dat文件来获取IP地址的所在地信息。 QQwry.dat是一个由QQ公司维护的IP数据库,它包含了全球大部分IP地址与其对应的城市、国家等地理位置信息。这个数据库文件是二进制格式,设计用于...
QQWry.Dat 是免费的 IP 库 相对于webservices,在本地用ip库根据ip判断地区是最快的。 net部落 http://www.ntbl.net