Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4166038
  • 博文数量: 291
  • 博客积分: 8003
  • 博客等级: 大校
  • 技术积分: 4275
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-30 18:28
文章分类

全部博文(291)

文章存档

2017年(1)

2013年(47)

2012年(115)

2011年(121)

2010年(7)

分类: C/C++

2011-06-11 01:39:22


纯真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. 作者:yifangyou

  6. 成功运行环境:CentOS 5 i386
  7.              gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)
  8. 本次测试使用的ip库是
  9.              
  10.                 记录总数:429555条
  11.                 更新日期:2011年06月05日
  12.                 数据库版本:纯真

  13. 输入参数:ip
  14.    当输入255.255.255.255显示数据库版本
  15.    
  16. 编译:
  17.   gcc -o iplocation iplocation.c

  18. 运行:
  19. [root@localhost ~]# ./iplocation 58.62.69.255
  20. ip=58.62.69.255 is between 58.62.64.0,58.62.69.255
  21. location:广东省广州市番禺区 电信
  22. [root@localhost ~]# ./iplocation 184.73.255.255
  23. ip=184.73.255.255 is between 184.72.0.0,184.73.255.255
  24. location:美国 弗吉尼亚州AmazonEC2东海岸数据中心
  25. [root@localhost ~]# ./iplocation 255.255.255.255
  26. ip=255.255.255.255 is between 255.255.255.0,255.255.255.255
  27. location:纯真网络 2011年06月05日IP数据
  28. [root@localhost ~]# ./iplocation 0.0.0.0
  29. ip=0.0.0.0 is between 0.0.0.0,0.255.255.255
  30. location:IANA保留地址 CZ88.NET

  31. *******/

  32. #include <sys/mman.h>
  33. #include <fcntl.h>
  34. #include <sys/types.h>
  35. #include <math.h>
  36. #include <unistd.h>
  37. #include <stdio.h>
  38. #include <string.h>
  39. #include <sys/stat.h>
  40. #include <netinet/in.h>
  41. #include <errno.h>
  42. #define SHARE_MEMORY_FILE "/tmp/qqwry.dat" //共享内存路径.ip库路径
  43. #define UNKNOWN "Unknown"
  44. #define SHARE_MEMORY_SIZE 10485760 //必须比ip库文件大
  45. #define INET6_ADDRSTRLEN 46
  46. #define RECORD_LEN 7 //单条记录长度
  47. //共享内存指针
  48. char *p_share;
  49. //第一条记录指针
  50. char *p_begin;
  51. char *p_end;
  52. //总记录数
  53. long total_record;

  54. //结果集
  55. typedef struct
  56. {
  57.     char *p_country;
  58.     char *p_area;
  59.     char beginip[INET6_ADDRSTRLEN]; // 用户IP所在范围的开始地址
  60.     char endip[INET6_ADDRSTRLEN]; // 用户IP所在范围的结束地址
  61. }location;
  62. //把4字节转为整数
  63. unsigned long getlong4(char *pos) //将读取的4个字节转化为长整型数
  64. {
  65.     unsigned long result=(((unsigned char )(*(pos+3)))<<24)
  66.      +(((unsigned char )(*(pos+2)))<<16)
  67.      +(((unsigned char )(*(pos+1)))<<8)
  68.      +((unsigned char )(*(pos)));
  69.     return result;
  70. }
  71. //把3字节转为整数
  72. unsigned long getlong3(char *pos) //将读取的3个字节转化为长整型数
  73. {
  74.     unsigned long result=(((unsigned char )(*(pos+2)))<<16)
  75.      +(((unsigned char )(*(pos+1)))<<8)
  76.      +((unsigned char )(*(pos)));
  77.     return result;
  78. }

  79. /**
  80.  * 创建共享内存,并加载ip库进去
  81.  *
  82.  * @return void
  83.  */
  84. void createshare()
  85. {
  86.      int fd;
  87.      long filesize=0;
  88.       FILE *fp=fopen(SHARE_MEMORY_FILE,"rb");
  89.       //读取文件长度
  90.       fseek(fp,0,SEEK_END);
  91.       filesize=ftell(fp);
  92.       //归零
  93.       fseek(fp,0,SEEK_SET);
  94.       //获得文件描述符,用于生成共享内存
  95.       fd=open(SHARE_MEMORY_FILE,O_CREAT|O_RDWR|O_TRUNC,00777);
  96.       p_share = (char*) mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
  97.       lseek(fd,0,SEEK_SET);
  98.       //把文件内容读入共享内存
  99.             fread(p_share,filesize,1,fp);
  100.             fclose(fp);
  101.          close(fd);
  102. }

  103. /**
  104.  * 打开共享内存指针
  105.  *
  106.  * @return void
  107.  */
  108. void openshare() // map a normal file as shared mem:
  109. {
  110.   int fd;
  111.   fd=open(SHARE_MEMORY_FILE,O_RDWR,00777);
  112.   //打开共享内存
  113.   p_share = (char*)mmap(NULL,SHARE_MEMORY_SIZE,PROT_READ,MAP_SHARED,fd,0);
  114.   if(p_share==MAP_FAILED)
  115.   {
  116.       //若是不存在则创建
  117.       createshare();    
  118.   }
  119.   close(fd);
  120.   //第一条记录位置
  121.   p_begin=p_share+getlong4(p_share);
  122.   //最后一条记录位置
  123.   p_end=p_share+getlong4(p_share+4);
  124.   //记录总数
  125.   total_record=(getlong4(p_share+4)-getlong4(p_share))/RECORD_LEN;
  126. }

  127. /**
  128.  * 关闭共享内存指针
  129.  *
  130.  * @return void
  131.  */
  132. void closeshare()
  133. {
  134.     munmap( p_share, SHARE_MEMORY_SIZE);    
  135. }

  136. /**
  137.  * 返回地区信息
  138.  *
  139.  * @char *pos 地区的指针
  140.  * @return char *
  141.  */
  142. char *getarea(char *pos) {
  143.         char *byte=pos; // 标志字节
  144.         pos++;
  145.         switch (*byte) {
  146.             case 0: // 没有区域信息
  147.                 return UNKNOWN;
  148.                 break;
  149.             case 1:
  150.             case 2: // 标志字节为1或2,表示区域信息被重定向
  151.                 return p_share+getlong3(pos);
  152.                 break;
  153.             default: // 否则,表示区域信息没有被重定向
  154.                 return byte;
  155.                 break;
  156.         }
  157.   }
  158. //获得ip所属地理信息,isp
  159. void getipinfo(char *ipstr,location *p_loc)
  160. {
  161.             char *pos = p_share;
  162.      int record_len=10;
  163.      char *firstip=0; // first record position
  164.      //把ip转为整数
  165.      unsigned long ip=htonl(inet_addr(ipstr));
  166.       firstip=p_begin;

  167.       long l=0;
  168.       long u=total_record;
  169.       long i=0;
  170.       char* findip=firstip;
  171.       unsigned long beginip=0;
  172.       unsigned long endip=0;
  173.       //二分法查找
  174.       while(l <= u)
  175.       {
  176.          i=(l+u)/2;
  177.            pos=firstip+i*RECORD_LEN;
  178.            beginip = getlong4(pos);
  179.            pos+=4;
  180.            if(ip<beginip)
  181.            {
  182.            u=i-1;    
  183.            }
  184.            else
  185.            {
  186.                 endip=getlong4(p_share+getlong3(pos));
  187.                 if(ip>endip)
  188.                 {
  189.                 l=i+1;        
  190.                 }
  191.                 else
  192.                 {
  193.                 findip=firstip+i*RECORD_LEN;
  194.                 break;    
  195.                 }
  196.            }
  197.       }
  198.       long offset = getlong3(findip+4);
  199.       pos=p_share+offset;
  200.       endip= getlong4(pos); // 用户IP所在范围的结束地址
  201.       pos+=4;

  202.       unsigned long j=ntohl(beginip);
  203.       inet_ntop(AF_INET,&j,p_loc->beginip, INET6_ADDRSTRLEN);// 获得开始地址的IP字符串类型
  204.       j=ntohl(endip);
  205.       inet_ntop(AF_INET,&j,p_loc->endip, INET6_ADDRSTRLEN);// 获得结束地址的IP字符串类型
  206.       
  207.       char *byte = pos; // 标志字节
  208.       pos++;
  209.       switch (*byte) {
  210.             case 1:{ // 标志字节为1,表示国家和区域信息都被同时重定向
  211.                 long countryOffset = getlong3(pos); // 重定向地址
  212.                 pos+=3;
  213.                 pos=p_share+countryOffset;
  214.                 byte = pos; // 标志字节
  215.                 pos++;
  216.                 switch (*byte) {
  217.                     case 2: // 标志字节为2,表示国家信息又被重定向
  218.                     {
  219.                             p_loc->p_country=p_share+getlong3(pos);
  220.                             pos=p_share+countryOffset+4;
  221.                         p_loc->p_area = getarea(pos);
  222.                     }
  223.                      break;
  224.                     default: // 否则,表示国家信息没有被重定向
  225.                     {
  226.                       p_loc->p_country=byte;
  227.                       p_loc->p_area = getarea(p_loc->p_country+strlen(p_loc->p_country)+1);
  228.                     }
  229.                         break;
  230.                 }
  231.             }
  232.             break;
  233.             case 2: // 标志字节为2,表示国家信息被重定向
  234.             {
  235.                 p_loc->p_country=p_share+getlong3(pos);
  236.                 p_loc->p_area=p_share+offset+8;
  237.             }
  238.             break;
  239.             default:{ // 否则,表示国家信息没有被重定向
  240.                 p_loc->p_country=byte;
  241.                 p_loc->p_area=getarea(p_loc->p_country+strlen(p_loc->p_country)+1);
  242.             }
  243.             break;
  244.       }

  245. }
  246. int main(int argc, char** argv)
  247. {
  248.       if(argc<2)
  249.       {
  250.           printf("please enter the checked ip.\n");    
  251.       }
  252.       location loc={0};
  253.       //打开共享内存
  254.             openshare();
  255.       getipinfo(argv[1],&loc);
  256.       printf("ip=%s is between %s,%s\n",argv[1],loc.beginip,loc.endip);
  257.       printf("location:%s %s\n",loc.p_country,loc.p_area);
  258.       //关闭共享内存
  259.   // closeshare();
  260.       return 0;
  261. }
运行测试结果:
qqwry.data:

end
阅读(3024) | 评论(2) | 转发(1) |
0

上一篇:zeromq安装手册

下一篇:windows下关闭mysql

给主人留下些什么吧!~~

452533212014-11-27 14:04:40

ip是4字节,不同的机器上,unsigned long长度不一致。如果unsigned long是8字节,你这比较就有问题。

452533212014-11-27 11:12:18

程序有BUG!某些ip查询错误