基本结构
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
qqwry.dat 纯真ip数据库 附在线升级工具 下载下来直接安装即可,ip数据库(qqwry.dat)在安装所在目录下
给定IP地址,在qqwry.DAT中查询对应的IP地址信息。 VS2010+WIN7
PHP调用QQWry.Dat 实现IP归属地查询 PHP调用QQWry.Dat 实现IP归属地查询
最新纯真ip数据库qqwry.dat
qqwry.dat纯真IP 2023年9月更新 2023年最新版 相对于上个版本最新更新超过了100000个IP信息
PHP 根据IP地址获取所在城市代码,通过调用纯真ip库qqwry.dat,判断所在城市。
包含一个QQWry.Dat,ShowIP.exe QQWry.Dat主要包含IP数据库 ShowIP.exe可以在线升级dat数据库,在可以查看IP地区
QQWry.Dat 是免费的 IP 库 相对于webservices,在本地用ip库根据ip判断地区是最快的。 net部落 http://www.ntbl.net
http://blog.csdn.net/thewindkee/article/details/70171711
QQWry.dat IP查询
qqwry.dat 20190705更新的地址库 全球地址库 很全很新,纯真IP库 QQ IP数据库 纯真版收集了包括中国电信、中国移动、中国联通、长城宽带、聚友宽带等 ISP 的最新准确 IP 地址数据。包括 最全的网吧数据。希望能够通过...
qqwry.bat,纯真ip库,根据IP获取地区运营商,2019-9-12上传,精准无误
qqwry.dat utf8格式
通过QQwry.dat来获的IP地址所在地 通过QQwry.dat来获的IP地址所在地
记录总数:529072条 更新日期:2021年04月21日 数据库版本:纯真
NET QQwry.dat IP地址设计经典代码NET QQwry.dat IP address design classic code
纯真数据库qqwry.dat文件下载 获取IP地址所在城市
QQWry.Dat ip库 收集了包括中国电信、中国网通、长城宽带、网通宽带、聚友宽带等 ISP 的最新准确 IP 地址数据。包括 最全的网吧数据。