使用共享内存查询纯真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;  

运行测试结果:

 

 


     本文转自yifangyou 51CTO博客,原文链接:http://blog.51cto.com/yifangyou/617658·,如需转载请自行联系原作者


相关文章
|
2月前
|
网络协议 安全 Unix
深入剖析进程间通信:Unix 套接字、共享内存与IP协议栈的性能比较
深入剖析进程间通信:Unix 套接字、共享内存与IP协议栈的性能比较
119 2
|
2月前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
54 0
|
2月前
|
C语言
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
|
8月前
|
Oracle 关系型数据库 Linux
解决在linux服务器上部署定时自动查找cpu,内存,磁盘使用量,并将查询结果写入数据库的脚本,只能手动运行实现插库操作
问题描述:将脚本名命名为mortior.sh(以下简称mo),手动执行脚本后查询数据库,表中有相应的信息,放入自动执行队列中,脚本被执行,但是查询数据库,并没有新增数据。
61 0
|
9月前
|
C语言
【进阶C语言】字符串与内存库函数认识与模拟实现(1)
size_t为无符号整形,接受他的返回值的变量类型也应该为size_t 函数参数就是字符指针类型。const为了修饰*str,防止原字符串的数据被修改。 需要包含的头文件为:#include&lt;string.h&gt;
37 0
|
2月前
|
存储 算法 C语言
C库函数详解 - 内存操作函数:memcpy()、memmove()、memset()、memcmp() (一)
`memcpy()` 和 `memmove()` 是C语言中的两个内存操作函数。 `memcpy()` 函数用于从源内存区域复制指定数量的字节到目标内存区域。它不处理内存重叠的情况,如果源和目标区域有重叠,结果是未定义的。函数原型如下: ```c void *memcpy(void *dest, const void *src, size_t num); ```
66 6
|
24天前
|
监控 Python
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息
paramiko 模块 ---Python脚本监控当前系统的CPU、内存、根目录、IP地址等信息
|
2月前
|
存储 C语言
C库函数详解 - 内存操作函数:memcpy()、memmove()、memset()、memcmp() (二)
`memset()`是一个C语言库函数,用于将指定内存区域的字节设置为特定值。函数原型为`void *memset(void *ptr, int value, size_t num)`,参数分别为指向内存起始位置的指针、要设置的值和设置的字节数。`memcmp()`函数则用于比较两个内存区域,返回值表示比较结果,原型为`int memcmp(const void *ptr1, const void *ptr2, size_t num)`。它比较指定字节数的内存,并根据比较结果返回整数值。
38 4
|
2月前
|
存储 Web App开发 运维
|
9月前
|
存储 编译器 C语言
【进阶C语言】字符串与内存库函数认识与模拟实现(2)
size_t为无符号整形,接受他的返回值的变量类型也应该为size_t 函数参数就是字符指针类型。const为了修饰*str,防止原字符串的数据被修改。 需要包含的头文件为:#include&lt;string.h&gt;
54 0