程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 使用共享內存查詢純真IP庫(微秒級)

使用共享內存查詢純真IP庫(微秒級)

編輯:關於C語言

純真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;  

運行測試結果:

 

 

本文出自 “一方有” 博客,請務必保留此出處http://yifangyou.blog.51cto.com/900206/617658

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved