使用共享内存查询纯真IP库(微秒级)

纯真IP库是网上一种比较完整的常用的ip库,基本上每5天更新一次。
我写了个程序通过把ip库加载到共享内存里,在42万条数据下,单次查询能够达到微秒级。

 

 
 
  1. /***** iplocation.c   
  2. 功能:本程序是把qq纯真ip数据库文件加载到共享内存里,通过参数查找出对应的所属的ip段,和地理位置,使用共享内存可以使查询一次在纳秒级。  
  3. qq纯真ip数据库文件格式可以查看:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html  
  4. qq纯真ip数据库官网下载地址:http://www.cz88.net/fox/ipdat.shtml,需要安装,安装完后把qqwry.dat拷出即可,也可从网上找。  
  5.  
  6. 作者:yifangyou  
  7.  
  8. 成功运行环境:CentOS 5 i386  
  9.              gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)  
  10. 本次测试使用的ip库是  
  11.                
  12.                 记录总数:429555条  
  13.                 更新日期:2011年06月05日  
  14.                 数据库版本:纯真  
  15.  
  16. 输入参数:ip  
  17.    当输入255.255.255.255显示数据库版本  
  18.      
  19. 编译:  
  20.   gcc -o iplocation iplocation.c  
  21.  
  22. 运行:   
  23. [root@localhost ~]# ./iplocation 58.62.69.255  
  24. ip=58.62.69.255 is between 58.62.64.0,58.62.69.255  
  25. location:广东省广州市番禺区 电信  
  26. [root@localhost ~]# ./iplocation 184.73.255.255  
  27. ip=184.73.255.255 is between 184.72.0.0,184.73.255.255  
  28. location:美国 弗吉尼亚州AmazonEC2东海岸数据中心  
  29. [root@localhost ~]# ./iplocation 255.255.255.255  
  30. ip=255.255.255.255 is between 255.255.255.0,255.255.255.255  
  31. location:纯真网络 2011年06月05日IP数据  
  32. [root@localhost ~]# ./iplocation 0.0.0.0  
  33. ip=0.0.0.0 is between 0.0.0.0,0.255.255.255  
  34. location:IANA保留地址 CZ88.NET  
  35.  
  36. *******/ 
  37.  
  38. #include <sys/mman.h>  
  39. #include <fcntl.h>  
  40. #include <sys/types.h>  
  41. #include <math.h>  
  42. #include <unistd.h>  
  43. #include <stdio.h>  
  44. #include <string.h>  
  45. #include <sys/stat.h>  
  46. #include <netinet/in.h>  
  47. #include <errno.h>   
  48. #define SHARE_MEMORY_FILE "/tmp/qqwry.dat" //共享内存路径.ip库路径  
  49. #define UNKNOWN "Unknown"  
  50. #define SHARE_MEMORY_SIZE 10485760 //必须比ip库文件大  
  51. #define INET6_ADDRSTRLEN 46  
  52. #define RECORD_LEN 7 //单条记录长度  
  53. //共享内存指针  
  54. char *p_share;  
  55. //第一条记录指针  
  56. char *p_begin;  
  57. char *p_end;  
  58. //总记录数  
  59. long total_record;  
  60.  
  61. //结果集  
  62. typedef struct   
  63. {  
  64.     char *p_country;  
  65.     char *p_area;  
  66.     char beginip[INET6_ADDRSTRLEN]; // 用户IP所在范围的开始地址  
  67.     char endip[INET6_ADDRSTRLEN]; // 用户IP所在范围的结束地址  
  68. }location;  
  69. //把4字节转为整数  
  70. unsigned long getlong4(char *pos) //将读取的4个字节转化为长整型数  
  71. {  
  72.     unsigned long result=(((unsigned char )(*(pos+3)))<<24)  
  73.      +(((unsigned char )(*(pos+2)))<<16)  
  74.      +(((unsigned char )(*(pos+1)))<<8)  
  75.      +((unsigned char )(*(pos)));  
  76.     return result;  
  77. }  
  78. //把3字节转为整数  
  79. unsigned long getlong3(char *pos) //将读取的3个字节转化为长整型数  
  80. {  
  81.     unsigned long result=(((unsigned char )(*(pos+2)))<<16)  
  82.      +(((unsigned char )(*(pos+1)))<<8)  
  83.      +((unsigned char )(*(pos)));  
  84.     return result;  
  85. }  
  86.  
  87. /**  
  88.  * 创建共享内存,并加载ip库进去  
  89.  *  
  90.  * @return void  
  91.  */ 
  92. void createshare()  
  93. {  
  94.      int fd;  
  95.      long filesize=0;  
  96.       FILE *fp=fopen(SHARE_MEMORY_FILE,"rb");  
  97.       //读取文件长度  
  98.       fseek(fp,0,SEEK_END);  
  99.       filesize=ftell(fp);  
  100.       //归零  
  101.       fseek(fp,0,SEEK_SET);  
  102.       //获得文件描述符,用于生成共享内存  
  103.       fd=open(SHARE_MEMORY_FILE,O_CREAT|O_RDWR|O_TRUNC,00777);  
  104.       p_share = (char*) mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );  
  105.       lseek(fd,0,SEEK_SET);  
  106.       //把文件内容读入共享内存  
  107.             fread(p_share,filesize,1,fp);  
  108.             fclose(fp);   
  109.          close(fd);   
  110. }  
  111.  
  112. /**  
  113.  * 打开共享内存指针  
  114.  *  
  115.  * @return void  
  116.  */ 
  117. void openshare() // map a normal file as shared mem:  
  118. {  
  119.   int fd;  
  120.   fd=open(SHARE_MEMORY_FILE,O_RDWR,00777);  
  121.   //打开共享内存  
  122.   p_share = (char*)mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ,MAP_SHARED,fd,0);  
  123.   if(p_share==MAP_FAILED)  
  124.   {  
  125.       //若是不存在则创建  
  126.       createshare();      
  127.   }  
  128.   close(fd);  
  129.   //第一条记录位置  
  130.   p_begin=p_share+getlong4(p_share);  
  131.   //最后一条记录位置  
  132.   p_end=p_share+getlong4(p_share+4);  
  133.   //记录总数  
  134.   total_record=(getlong4(p_share+4)-getlong4(p_share))/RECORD_LEN;  
  135. }  
  136.  
  137. /**  
  138.  * 关闭共享内存指针  
  139.  *  
  140.  * @return void  
  141.  */ 
  142. void closeshare()  
  143. {  
  144.     munmap( p_share, SHARE_MEMORY_SIZE);      
  145. }  
  146.  
  147. /**  
  148.  * 返回地区信息  
  149.  *  
  150.  * @char *pos 地区的指针  
  151.  * @return char *  
  152.  */ 
  153. char *getarea(char *pos) {  
  154.         char *byte=pos; // 标志字节  
  155.         pos++;  
  156.         switch (*byte) {  
  157.             case 0: // 没有区域信息  
  158.                 return UNKNOWN;  
  159.                 break;  
  160.             case 1:  
  161.             case 2: // 标志字节为1或2,表示区域信息被重定向  
  162.                 return p_share+getlong3(pos);  
  163.                 break;  
  164.             default// 否则,表示区域信息没有被重定向  
  165.                 return byte;  
  166.                 break;  
  167.         }  
  168.   }  
  169. //获得ip所属地理信息,isp  
  170. void getipinfo(char *ipstr,location *p_loc)  
  171. {  
  172.             char *pos = p_share;  
  173.      int record_len=10;  
  174.      char *firstip=0; // first record position  
  175.      //把ip转为整数  
  176.      unsigned long ip=htonl(inet_addr(ipstr));  
  177.       firstip=p_begin;  
  178.  
  179.       long l=0;  
  180.       long u=total_record;  
  181.       long i=0;  
  182.       char* findip=firstip;  
  183.       unsigned long beginip=0;  
  184.       unsigned long endip=0;  
  185.       //二分法查找  
  186.       while(l <= u)  
  187.       {  
  188.          i=(l+u)/2;  
  189.            pos=firstip+i*RECORD_LEN;  
  190.            beginip = getlong4(pos);  
  191.            pos+=4;  
  192.            if(ip<beginip)  
  193.            {  
  194.            u=i-1;      
  195.            }  
  196.            else 
  197.            {  
  198.                 endip=getlong4(p_share+getlong3(pos));  
  199.                 if(ip>endip)  
  200.                 {  
  201.                 l=i+1;          
  202.                 }  
  203.                 else 
  204.                 {  
  205.                 findip=firstip+i*RECORD_LEN;  
  206.                 break;      
  207.                 }  
  208.            }  
  209.       }  
  210.       long offset = getlong3(findip+4);  
  211.       pos=p_share+offset;  
  212.       endip= getlong4(pos); // 用户IP所在范围的结束地址  
  213.       pos+=4;  
  214.  
  215.       unsigned long j=ntohl(beginip);  
  216.       inet_ntop(AF_INET,&j,p_loc->beginip, INET6_ADDRSTRLEN);// 获得开始地址的IP字符串类型  
  217.       j=ntohl(endip);  
  218.       inet_ntop(AF_INET,&j,p_loc->endip, INET6_ADDRSTRLEN);// 获得结束地址的IP字符串类型  
  219.         
  220.       char *byte = pos; // 标志字节  
  221.       pos++;  
  222.       switch (*byte) {  
  223.             case 1:{ // 标志字节为1,表示国家和区域信息都被同时重定向  
  224.                 long countryOffset = getlong3(pos); // 重定向地址  
  225.                 pos+=3;  
  226.                 pos=p_share+countryOffset;  
  227.                 byte = pos; // 标志字节  
  228.                 pos++;  
  229.                 switch (*byte) {  
  230.                     case 2: // 标志字节为2,表示国家信息又被重定向  
  231.                     {  
  232.                             p_loc->p_country=p_share+getlong3(pos);  
  233.                             pos=p_share+countryOffset+4;  
  234.                         p_loc->p_area = getarea(pos);  
  235.                     }  
  236.                      break;  
  237.                     default// 否则,表示国家信息没有被重定向  
  238.                     {  
  239.                       p_loc->p_country=byte;  
  240.                       p_loc->p_area = getarea(p_loc->p_country+strlen(p_loc->p_country)+1);  
  241.                     }  
  242.                         break;  
  243.                 }  
  244.             }  
  245.             break;  
  246.             case 2: // 标志字节为2,表示国家信息被重定向  
  247.             {  
  248.                 p_loc->p_country=p_share+getlong3(pos);  
  249.                 p_loc->p_area=p_share+offset+8;  
  250.             }  
  251.             break;  
  252.             default:{ // 否则,表示国家信息没有被重定向  
  253.                 p_loc->p_country=byte;  
  254.                 p_loc->p_area=getarea(p_loc->p_country+strlen(p_loc->p_country)+1);  
  255.             }  
  256.             break;  
  257.       }  
  258.  
  259. }  
  260. int main(int argc, char** argv)  
  261. {  
  262.       if(argc<2)  
  263.       {  
  264.           printf("please enter the checked ip.\n");      
  265.       }  
  266.       location loc={0};  
  267.       //打开共享内存  
  268.             openshare();  
  269.       getipinfo(argv[1],&loc);  
  270.       printf("ip=%s is between %s,%s\n",argv[1],loc.beginip,loc.endip);  
  271.       printf("location:%s %s\n",loc.p_country,loc.p_area);  
  272.       //关闭共享内存  
  273.   // closeshare();  
  274.       return 0;  

运行测试结果:

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值