- /***** 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;
- }
运行测试结果:
本文出自 “一方有” 博客,请务必保留此出处http://yifangyou.blog.51cto.com/900206/617658