纯真IP库是网上一种比较完整的常用的ip库,基本上每5天更新一次。
我写了个程序通过把ip库加载到共享内存里,在42万条数据下,单次查询能够达到微秒级。
- /***** iplocation.c
-
功能:本程序是把qq纯真ip数据库文件加载到共享内存里,通过参数查找出对应的所属的ip段,和地理位置,使用共享内存可以使查询一次在纳秒级。
-
qq纯真ip数据库文件格式可以查看:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
- qq纯真ip数据库官网下载地址:http://www.cz88.net/fox/ipdat.shtml,需要安装,安装完后把qqwry.dat拷出即可,也可从网上找。
-
-
作者:yifangyou
-
-
成功运行环境:CentOS 5 i386
-
gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)
-
本次测试使用的ip库是
-
-
记录总数:429555条
-
更新日期:2011年06月05日
-
数据库版本:纯真
-
-
输入参数:ip
-
当输入255.255.255.255显示数据库版本
-
-
编译:
-
gcc -o iplocation iplocation.c
-
-
运行:
-
[root@localhost ~]# ./iplocation 58.62.69.255
-
ip=58.62.69.255 is between 58.62.64.0,58.62.69.255
-
location:广东省广州市番禺区 电信
-
[root@localhost ~]# ./iplocation 184.73.255.255
-
ip=184.73.255.255 is between 184.72.0.0,184.73.255.255
-
location:美国 弗吉尼亚州AmazonEC2东海岸数据中心
-
[root@localhost ~]# ./iplocation 255.255.255.255
-
ip=255.255.255.255 is between 255.255.255.0,255.255.255.255
-
location:纯真网络 2011年06月05日IP数据
-
[root@localhost ~]# ./iplocation 0.0.0.0
-
ip=0.0.0.0 is between 0.0.0.0,0.255.255.255
-
location:IANA保留地址 CZ88.NET
-
-
*******/
-
-
#include <sys/mman.h>
-
#include <fcntl.h>
-
#include <sys/types.h>
-
#include <math.h>
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <string.h>
-
#include <sys/stat.h>
-
#include <netinet/in.h>
-
#include <errno.h>
-
#define SHARE_MEMORY_FILE "/tmp/qqwry.dat" //共享内存路径.ip库路径
-
#define UNKNOWN "Unknown"
-
#define SHARE_MEMORY_SIZE 10485760 //必须比ip库文件大
-
#define INET6_ADDRSTRLEN 46
-
#define RECORD_LEN 7 //单条记录长度
-
//共享内存指针
-
char *p_share;
-
//第一条记录指针
-
char *p_begin;
-
char *p_end;
-
//总记录数
-
long total_record;
-
-
//结果集
-
typedef struct
-
{
-
char *p_country;
-
char *p_area;
-
char beginip[INET6_ADDRSTRLEN]; // 用户IP所在范围的开始地址
-
char endip[INET6_ADDRSTRLEN]; // 用户IP所在范围的结束地址
-
}location;
-
//把4字节转为整数
-
unsigned long getlong4(char *pos) //将读取的4个字节转化为长整型数
-
{
-
unsigned long result=(((unsigned char )(*(pos+3)))<<24)
-
+(((unsigned char )(*(pos+2)))<<16)
-
+(((unsigned char )(*(pos+1)))<<8)
-
+((unsigned char )(*(pos)));
-
return result;
-
}
-
//把3字节转为整数
-
unsigned long getlong3(char *pos) //将读取的3个字节转化为长整型数
-
{
-
unsigned long result=(((unsigned char )(*(pos+2)))<<16)
-
+(((unsigned char )(*(pos+1)))<<8)
-
+((unsigned char )(*(pos)));
-
return result;
-
}
-
-
/**
-
* 创建共享内存,并加载ip库进去
-
*
-
* @return void
-
*/
-
void createshare()
-
{
-
int fd;
-
long filesize=0;
-
FILE *fp=fopen(SHARE_MEMORY_FILE,"rb");
-
//读取文件长度
-
fseek(fp,0,SEEK_END);
-
filesize=ftell(fp);
-
//归零
-
fseek(fp,0,SEEK_SET);
-
//获得文件描述符,用于生成共享内存
-
fd=open(SHARE_MEMORY_FILE,O_CREAT|O_RDWR|O_TRUNC,00777);
-
p_share = (char*) mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
-
lseek(fd,0,SEEK_SET);
-
//把文件内容读入共享内存
-
fread(p_share,filesize,1,fp);
-
fclose(fp);
-
close(fd);
-
}
-
-
/**
-
* 打开共享内存指针
-
*
-
* @return void
-
*/
-
void openshare() // map a normal file as shared mem:
-
{
-
int fd;
-
fd=open(SHARE_MEMORY_FILE,O_RDWR,00777);
-
//打开共享内存
-
p_share = (char*)mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ,MAP_SHARED,fd,0);
-
if(p_share==MAP_FAILED)
-
{
-
//若是不存在则创建
-
createshare();
-
}
-
close(fd);
-
//第一条记录位置
-
p_begin=p_share+getlong4(p_share);
-
//最后一条记录位置
-
p_end=p_share+getlong4(p_share+4);
-
//记录总数
-
total_record=(getlong4(p_share+4)-getlong4(p_share))/RECORD_LEN;
-
}
-
-
/**
-
* 关闭共享内存指针
-
*
-
* @return void
-
*/
-
void closeshare()
-
{
-
munmap( p_share, SHARE_MEMORY_SIZE);
-
}
-
-
/**
-
* 返回地区信息
-
*
-
* @char *pos 地区的指针
-
* @return char *
-
*/
-
char *getarea(char *pos) {
-
char *byte=pos; // 标志字节
-
pos++;
-
switch (*byte) {
-
case 0: // 没有区域信息
-
return UNKNOWN;
-
break;
-
case 1:
-
case 2: // 标志字节为1或2,表示区域信息被重定向
-
return p_share+getlong3(pos);
-
break;
-
default: // 否则,表示区域信息没有被重定向
-
return byte;
-
break;
-
}
-
}
-
//获得ip所属地理信息,isp
-
void getipinfo(char *ipstr,location *p_loc)
-
{
-
char *pos = p_share;
-
int record_len=10;
-
char *firstip=0; // first record position
-
//把ip转为整数
-
unsigned long ip=htonl(inet_addr(ipstr));
-
firstip=p_begin;
-
-
long l=0;
-
long u=total_record;
-
long i=0;
-
char* findip=firstip;
-
unsigned long beginip=0;
-
unsigned long endip=0;
-
//二分法查找
-
while(l <= u)
-
{
-
i=(l+u)/2;
-
pos=firstip+i*RECORD_LEN;
-
beginip = getlong4(pos);
-
pos+=4;
-
if(ip<beginip)
-
{
-
u=i-1;
-
}
-
else
-
{
-
endip=getlong4(p_share+getlong3(pos));
-
if(ip>endip)
-
{
-
l=i+1;
-
}
-
else
-
{
-
findip=firstip+i*RECORD_LEN;
-
break;
-
}
-
}
-
}
-
long offset = getlong3(findip+4);
-
pos=p_share+offset;
-
endip= getlong4(pos); // 用户IP所在范围的结束地址
-
pos+=4;
-
-
unsigned long j=ntohl(beginip);
-
inet_ntop(AF_INET,&j,p_loc->beginip, INET6_ADDRSTRLEN);// 获得开始地址的IP字符串类型
-
j=ntohl(endip);
-
inet_ntop(AF_INET,&j,p_loc->endip, INET6_ADDRSTRLEN);// 获得结束地址的IP字符串类型
-
-
char *byte = pos; // 标志字节
-
pos++;
-
switch (*byte) {
-
case 1:{ // 标志字节为1,表示国家和区域信息都被同时重定向
-
long countryOffset = getlong3(pos); // 重定向地址
-
pos+=3;
-
pos=p_share+countryOffset;
-
byte = pos; // 标志字节
-
pos++;
-
switch (*byte) {
-
case 2: // 标志字节为2,表示国家信息又被重定向
-
{
-
p_loc->p_country=p_share+getlong3(pos);
-
pos=p_share+countryOffset+4;
-
p_loc->p_area = getarea(pos);
-
}
-
break;
-
default: // 否则,表示国家信息没有被重定向
-
{
-
p_loc->p_country=byte;
-
p_loc->p_area = getarea(p_loc->p_country+strlen(p_loc->p_country)+1);
-
}
-
break;
-
}
-
}
-
break;
-
case 2: // 标志字节为2,表示国家信息被重定向
-
{
-
p_loc->p_country=p_share+getlong3(pos);
-
p_loc->p_area=p_share+offset+8;
-
}
-
break;
-
default:{ // 否则,表示国家信息没有被重定向
-
p_loc->p_country=byte;
-
p_loc->p_area=getarea(p_loc->p_country+strlen(p_loc->p_country)+1);
-
}
-
break;
-
}
-
-
}
-
int main(int argc, char** argv)
-
{
-
if(argc<2)
-
{
-
printf("please enter the checked ip.\n");
-
}
-
location loc={0};
-
//打开共享内存
-
openshare();
-
getipinfo(argv[1],&loc);
-
printf("ip=%s is between %s,%s\n",argv[1],loc.beginip,loc.endip);
-
printf("location:%s %s\n",loc.p_country,loc.p_area);
-
//关闭共享内存
-
// closeshare();
-
return 0;
-
}
运行测试结果:
qqwry.data:
end
阅读(3118) | 评论(2) | 转发(1) |