转载纯真ip库

http://blog.csdn.net/clin003/archive/2007/08/14/1743157.aspx

利用 QQWry.Dat 实现 IP 地址高效检索(PHP)
根据 LumaQQ 开发者文档中的纯真 IP 数据库格式详解,我编写了一个 PHP 的查询
IP 所在地区信息的类。在编写过程中发现纯真 IP 数据库格式详解中关于记录区的描述不是很全面,不过出入也不是很大,所以我没必要再写一份纯真 IP
数据库的格式说明了,大家感兴趣的话,读一读下面的代码应该就能看出来了。代码中加了很详细的注释,应该很容易读懂的。

在创建这个类的一个实例后,实例中就保存了打开的文件指针和一些查询需要的信息,每次查询时不需要重新打开文件,直到页面执行结束后,打开的文件才会自动关闭。这样。在一个页面内进行多次查询时,效率是很高的。并且此类不仅可以直接查询
IP,还可以自动将域名解析为 IP 进行查询。

下面是程序代码:

Java代码  

  1. <?php
  2. /**
  3. * IP 地理位置查询类
  4. *
  5. * @author 马秉尧
  6. * @version 1.5
  7. * @copyright 2005 CoolCode.CN
  8. */
  9. class IpLocation {
  10. /**
  11. * QQWry.Dat文件指针
  12. *
  13. * @var resource
  14. */
  15. var $fp;
  16. /**
  17. * 第一条IP记录的偏移地址
  18. *
  19. * @var int
  20. */
  21. var $firstip;
  22. /**
  23. * 最后一条IP记录的偏移地址
  24. *
  25. * @var int
  26. */
  27. var $lastip;
  28. /**
  29. * IP记录的总条数(不包含版本信息记录)
  30. *
  31. * @var int
  32. */
  33. var $totalip;
  34. /**
  35. * 返回读取的长整型数
  36. *
  37. * @access private
  38. * @return int
  39. */
  40. function getlong() {
  41. //将读取的little-endian编码的4个字节转化为长整型数
  42. $result = unpack(‘Vlong‘, fread($this->fp, 4));
  43. return $result[‘long‘];
  44. }
  45. /**
  46. * 返回读取的3个字节的长整型数
  47. *
  48. * @access private
  49. * @return int
  50. */
  51. function getlong3() {
  52. //将读取的little-endian编码的3个字节转化为长整型数
  53. $result = unpack(‘Vlong‘, fread($this->fp, 3).chr(0));
  54. return $result[‘long‘];
  55. }
  56. /**
  57. * 返回压缩后可进行比较的IP地址
  58. *
  59. * @access private
  60. * @param string $ip
  61. * @return string
  62. */
  63. function packip($ip) {
  64. // 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
  65. // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
  66. return pack(‘N‘, intval(ip2long($ip)));
  67. }
  68. /**
  69. * 返回读取的字符串
  70. *
  71. * @access private
  72. * @param string $data
  73. * @return string
  74. */
  75. function getstring($data = "") {
  76. $char = fread($this->fp, 1);
  77. while (ord($char) > 0) {        // 字符串按照C格式保存,以?结束
  78. $data .= $char;             // 将读取的字符连接到给定字符串之后
  79. $char = fread($this->fp, 1);
  80. }
  81. return $data;
  82. }
  83. /**
  84. * 返回地区信息
  85. *
  86. * @access private
  87. * @return string
  88. */
  89. function getarea() {
  90. $byte = fread($this->fp, 1);    // 标志字节
  91. switch (ord($byte)) {
  92. case 0:                     // 没有区域信息
  93. $area = "";
  94. break;
  95. case 1:
  96. case 2:                     // 标志字节为1或2,表示区域信息被重定向
  97. fseek($this->fp, $this->getlong3());
  98. $area = $this->getstring();
  99. break;
  100. default:                    // 否则,表示区域信息没有被重定向
  101. $area = $this->getstring($byte);
  102. break;
  103. }
  104. return $area;
  105. }
  106. /**
  107. * 根据所给 IP 地址或域名返回所在地区信息
  108. *
  109. * @access public
  110. * @param string $ip
  111. * @return array
  112. */
  113. function getlocation($ip) {
  114. if (!$this->fp) return null;            // 如果数据文件没有被正确打开,则直接返回空
  115. $location[‘ip‘] = gethostbyname($ip);   // 将输入的域名转化为IP地址
  116. $ip = $this->packip($location[‘ip‘]);   // 将输入的IP地址转化为可比较的IP地址
  117. // 不合法的IP地址会被转化为255.255.255.255
  118. // 对分搜索
  119. $l = 0;                         // 搜索的下边界
  120. $u = $this->totalip;            // 搜索的上边界
  121. $findip = $this->lastip;        // 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
  122. while ($l <= $u) {              // 当上边界小于下边界时,查找失败
  123. $i = floor(($l + $u) / 2);  // 计算近似中间记录
  124. fseek($this->fp, $this->firstip + $i * 7);
  125. $beginip = strrev(fread($this->fp, 4));     // 获取中间记录的开始IP地址
  126. // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式
  127. // 以便用于比较,后面相同。
  128. if ($ip < $beginip) {       // 用户的IP小于中间记录的开始IP地址时
  129. $u = $i - 1;            // 将搜索的上边界修改为中间记录减一
  130. }
  131. else {
  132. fseek($this->fp, $this->getlong3());
  133. $endip = strrev(fread($this->fp, 4));   // 获取中间记录的结束IP地址
  134. if ($ip > $endip) {     // 用户的IP大于中间记录的结束IP地址时
  135. $l = $i + 1;        // 将搜索的下边界修改为中间记录加一
  136. }
  137. else {                  // 用户的IP在中间记录的IP范围内时
  138. $findip = $this->firstip + $i * 7;
  139. break;              // 则表示找到结果,退出循环
  140. }
  141. }
  142. }
  143. //获取查找到的IP地理位置信息
  144. fseek($this->fp, $findip);
  145. $location[‘beginip‘] = long2ip($this->getlong());   // 用户IP所在范围的开始地址
  146. $offset = $this->getlong3();
  147. fseek($this->fp, $offset);
  148. $location[‘endip‘] = long2ip($this->getlong());     // 用户IP所在范围的结束地址
  149. $byte = fread($this->fp, 1);    // 标志字节
  150. switch (ord($byte)) {
  151. case 1:                     // 标志字节为1,表示国家和区域信息都被同时重定向
  152. $countryOffset = $this->getlong3();         // 重定向地址
  153. fseek($this->fp, $countryOffset);
  154. $byte = fread($this->fp, 1);    // 标志字节
  155. switch (ord($byte)) {
  156. case 2:             // 标志字节为2,表示国家信息又被重定向
  157. fseek($this->fp, $this->getlong3());
  158. $location[‘country‘] = $this->getstring();
  159. fseek($this->fp, $countryOffset + 4);
  160. $location[‘area‘] = $this->getarea();
  161. break;
  162. default:            // 否则,表示国家信息没有被重定向
  163. $location[‘country‘] = $this->getstring($byte);
  164. $location[‘area‘] = $this->getarea();
  165. break;
  166. }
  167. break;
  168. case 2:                     // 标志字节为2,表示国家信息被重定向
  169. fseek($this->fp, $this->getlong3());
  170. $location[‘country‘] = $this->getstring();
  171. fseek($this->fp, $offset + 8);
  172. $location[‘area‘] = $this->getarea();
  173. break;
  174. default:                    // 否则,表示国家信息没有被重定向
  175. $location[‘country‘] = $this->getstring($byte);
  176. $location[‘area‘] = $this->getarea();
  177. break;
  178. }
  179. if ($location[‘country‘] == " CZ88.NET") {  // CZ88.NET表示没有有效信息
  180. $location[‘country‘] = "未知";
  181. }
  182. if ($location[‘area‘] == " CZ88.NET") {
  183. $location[‘area‘] = "";
  184. }
  185. return $location;
  186. }
  187. /**
  188. * 构造函数,打开 QQWry.Dat 文件并初始化类中的信息
  189. *
  190. * @param string $filename
  191. * @return IpLocation
  192. */
  193. function IpLocation($filename = "QQWry.Dat") {
  194. $this->fp = 0;
  195. if (($this->fp = @fopen($filename, ‘rb‘)) !== false) {
  196. $this->firstip = $this->getlong();
  197. $this->lastip = $this->getlong();
  198. $this->totalip = ($this->lastip - $this->firstip) / 7;
  199. //注册析构函数,使其在程序执行结束时执行
  200. register_shutdown_function(array(&$this, ‘_IpLocation‘));
  201. }
  202. }
  203. /**
  204. * 析构函数,用于在页面执行结束后自动关闭打开的文件。
  205. *
  206. */
  207. function _IpLocation() {
  208. if ($this->fp) {
  209. fclose($this->fp);
  210. }
  211. $this->fp = 0;
  212. }
  213. }
  214. ?>

Java代码  

  1. Discuz  5.0 不在使用自己的IP数据,而是使用纯真IP的数据格式, 存取纯真IP数据库稍微有点麻烦,它的存储格式比较特殊也很有趣,具体的格式分析参考下面两个链接,其他语言实现参考文章末的链接。
  2. 《纯真IP数据库格式详解》
  3. 链接一:http://blog.csdn.Net/heiyeshuwu/archive/2006/05/12/725675.aspx
  4. 链接二:http://lumaqq.Linuxsir.org/article/qqwry_format_detail.html
  5. 纯真IP数据库官网:http://www.cz88.Net/ip/
  6. 纯真IP数据库下载:http://update.cz88.Net/soft/qqwry.rar
  7. 以下函数conrvertip()位于 Discuz!5_GBK/upload/include/misc.func.Php 路径中,有兴趣可以具体去阅读分析。(下面代码我做了简单的修改,更便于阅读,核心没有修改)
  8. <?
  9. //===================================
  10. //
  11. // 功能:IP地址获取真实地址函数
  12. // 参数:$ip - IP地址
  13. // 作者:[Discuz!] (C) Comsenz Inc.
  14. //
  15. //===================================
  16. function convertip($ip) {
  17. //IP数据文件路径
  18. $dat_path = ‘QQWry.Dat‘;
  19. //检查IP地址
  20. if(!preg_match("/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/", $ip)) {
  21. return ‘IP Address Error‘;
  22. }
  23. //打开IP数据文件
  24. if(!$fd = @fopen($dat_path, ‘rb‘)){
  25. return ‘IP date file not exists or access denied‘;
  26. }
  27. //分解IP进行运算,得出整形数
  28. $ip = explode(‘.‘, $ip);
  29. $ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];
  30. //获取IP数据索引开始和结束位置
  31. $DataBegin = fread($fd, 4);
  32. $DataEnd = fread($fd, 4);
  33. $ipbegin = implode(‘‘, unpack(‘L‘, $DataBegin));
  34. if($ipbegin < 0) $ipbegin += pow(2, 32);
  35. $ipend = implode(‘‘, unpack(‘L‘, $DataEnd));
  36. if($ipend < 0) $ipend += pow(2, 32);
  37. $ipAllNum = ($ipend - $ipbegin) / 7 + 1;
  38. $BeginNum = 0;
  39. $EndNum = $ipAllNum;
  40. //使用二分查找法从索引记录中搜索匹配的IP记录
  41. while($ip1num>$ipNum || $ip2num<$ipNum) {
  42. $Middle= intval(($EndNum + $BeginNum) / 2);
  43. //偏移指针到索引位置读取4个字节
  44. fseek($fd, $ipbegin + 7 * $Middle);
  45. $ipData1 = fread($fd, 4);
  46. if(strlen($ipData1) < 4) {
  47. fclose($fd);
  48. return ‘System Error‘;
  49. }
  50. //提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
  51. $ip1num = implode(‘‘, unpack(‘L‘, $ipData1));
  52. if($ip1num < 0) $ip1num += pow(2, 32);
  53. //提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
  54. if($ip1num > $ipNum) {
  55. $EndNum = $Middle;
  56. continue;
  57. }
  58. //取完上一个索引后取下一个索引
  59. $DataSeek = fread($fd, 3);
  60. if(strlen($DataSeek) < 3) {
  61. fclose($fd);
  62. return ‘System Error‘;
  63. }
  64. $DataSeek = implode(‘‘, unpack(‘L‘, $DataSeek.chr(0)));
  65. fseek($fd, $DataSeek);
  66. $ipData2 = fread($fd, 4);
  67. if(strlen($ipData2) < 4) {
  68. fclose($fd);
  69. return ‘System Error‘;
  70. }
  71. $ip2num = implode(‘‘, unpack(‘L‘, $ipData2));
  72. if($ip2num < 0) $ip2num += pow(2, 32);
  73. //没找到提示未知
  74. if($ip2num < $ipNum) {
  75. if($Middle == $BeginNum) {
  76. fclose($fd);
  77. return ‘Unknown‘;
  78. }
  79. $BeginNum = $Middle;
  80. }
  81. }
  82. //下面的代码读晕了,没读明白,有兴趣的慢慢读
  83. $ipFlag = fread($fd, 1);
  84. if($ipFlag == chr(1)) {
  85. $ipSeek = fread($fd, 3);
  86. if(strlen($ipSeek) < 3) {
  87. fclose($fd);
  88. return ‘System Error‘;
  89. }
  90. $ipSeek = implode(‘‘, unpack(‘L‘, $ipSeek.chr(0)));
  91. fseek($fd, $ipSeek);
  92. $ipFlag = fread($fd, 1);
  93. }
  94. if($ipFlag == chr(2)) {
  95. $AddrSeek = fread($fd, 3);
  96. if(strlen($AddrSeek) < 3) {
  97. fclose($fd);
  98. return ‘System Error‘;
  99. }
  100. $ipFlag = fread($fd, 1);
  101. if($ipFlag == chr(2)) {
  102. $AddrSeek2 = fread($fd, 3);
  103. if(strlen($AddrSeek2) < 3) {
  104. fclose($fd);
  105. return ‘System Error‘;
  106. }
  107. $AddrSeek2 = implode(‘‘, unpack(‘L‘, $AddrSeek2.chr(0)));
  108. fseek($fd, $AddrSeek2);
  109. } else {
  110. fseek($fd, -1, SEEK_CUR);
  111. }
  112. while(($char = fread($fd, 1)) != chr(0))
  113. $ipAddr2 .= $char;
  114. $AddrSeek = implode(‘‘, unpack(‘L‘, $AddrSeek.chr(0)));
  115. fseek($fd, $AddrSeek);
  116. while(($char = fread($fd, 1)) != chr(0))
  117. $ipAddr1 .= $char;
  118. } else {
  119. fseek($fd, -1, SEEK_CUR);
  120. while(($char = fread($fd, 1)) != chr(0))
  121. $ipAddr1 .= $char;
  122. $ipFlag = fread($fd, 1);
  123. if($ipFlag == chr(2)) {
  124. $AddrSeek2 = fread($fd, 3);
  125. if(strlen($AddrSeek2) < 3) {
  126. fclose($fd);
  127. return ‘System Error‘;
  128. }
  129. $AddrSeek2 = implode(‘‘, unpack(‘L‘, $AddrSeek2.chr(0)));
  130. fseek($fd, $AddrSeek2);
  131. } else {
  132. fseek($fd, -1, SEEK_CUR);
  133. }
  134. while(($char = fread($fd, 1)) != chr(0)){
  135. $ipAddr2 .= $char;
  136. }
  137. }
  138. fclose($fd);
  139. //最后做相应的替换操作后返回结果
  140. if(preg_match(‘/http/i‘, $ipAddr2)) {
  141. $ipAddr2 = ‘‘;
  142. }
  143. $ipaddr = "$ipAddr1 $ipAddr2";
  144. $ipaddr = preg_replace(‘/CZ88.Net/is‘, ‘‘, $ipaddr);
  145. $ipaddr = preg_replace(‘/^s*/is‘, ‘‘, $ipaddr);
  146. $ipaddr = preg_replace(‘/s*$/is‘, ‘‘, $ipaddr);
  147. if(preg_match(‘/http/i‘, $ipaddr) || $ipaddr == ‘‘) {
  148. $ipaddr = ‘Unknown‘;
  149. }
  150. return $ipaddr;
  151. }
  152. //========================
  153. //
  154. //  调用举例(速度很快)
  155. //
  156. //========================
  157. echo convertip(‘219.238.235.10‘);
  158. //输出: 北京市 电信通
  159. echo convertip(‘23.56.82.12‘);
  160. //输出:IANA
  161. echo convertip(‘250.69.52.0‘);
  162. //输出:IANA保留地址
  163. echo convertip(‘238.69.52.0‘);
  164. //输出:IANA保留地址 用于多点传送
  165. echo convertip(‘192.168.0.1‘);
  166. //输出:局域网 对方和您在同一内部网
  167. echo convertip(‘255.255.255.255‘);
  168. //输出:纯真网络 2006年11月20日IP数据
  169. ?>

附:(相应其他实现程序)

Php)
"
 href=
"
http
:
//
www.coolcode.cn/?p=16" rel=bookmark>利用 QQWry.Dat 实现 IP 地址高效检索(Php)(作者: andot)

数据库(QQWry
.
Dat)查询 C源码
"
 href=
"
http
:
//
www.douzi.org/wp/index.Php/articles/71" rel=bookmark>纯真IP数据库(QQWry.Dat)查询 C源码 (作者:Windix)

时间: 2024-10-11 05:00:57

转载纯真ip库的相关文章

lib-qqwry v1.0 发布 nodejs解析纯真IP库(qqwry.dat)

lib-qqwry是当初学习node时用来练手的一个模块,用来解析纯真IP库的 现在发一个v1.0版本弥补我当时稚嫩的代码. 意外收获是,整理代码后发现,相比v0.x版本 急速模式下的效率提升大概20%; 但 v1.0 相比 v0.x 有很大改动,并不兼容之前的版本,所以升级碰到问题的同学需要好好了解一下. 1.初始化 v0.x 初使化的初衷是让用户可以使用指定版本的IP库文件, 在最合适的时候把IP库加载到内存中进行高速查询; v1.0 v1.0改变了程序结构,实现方式; 不将IP库加载到内存

qqwry - 纯真ip库的golang服务

qqwry 纯真 IP 库的一个服务.通过http提供一个ip地址归属地查询支持 软件介绍 我们大家做网站的时候,都会需要将用户的IP地址转换为归属地址功能,而之前的作法大都是从硬盘的数据文件中读取,这样不太高效.我这次将纯真IP库直接加载到内存中,并以http服务的方式提供接口出来.这样方便自己项目中所有的程序都能方便的接口IP查询功能了. 安装 go安装 go get github.com/freshcn/qqwry 二进制包直接下载 https://github.com/freshcn/q

通过纯真IP库,查询出IP地址的归属地

原文:通过纯真IP库,查询出IP地址的归属地 源代码下载地址:http://www.zuidaima.com/share/1550463698422784.htm 项目的编码要设置为utf-8,否则有乱码

解析纯真IP地址库

一周以来,一直在做 IP地址库的解析.从调研到编码到优化,大概花了有七八天的时间.感觉很好玩.总结一下整个做的过程. 1.关于IP 地址库的解析方式 目前主要的解析方式有两种:通过API,或通过IP数据库. API方式很简单,目前国内大厂不少提供API接口,只要发送请求的IP,就能获得相应的地理位置.像BAT等等公司都提供IP查询接口.这种解析方式的好处在于,编码简单,一个请求获得数据,然后解析一下就好了(通常只是个json数据),而且不用维护数据库,对本地没有负担.但是缺点也挺明显的,首先是慢

ThinkPHP使用纯真IP获取物理地址时中文乱码问题

今天在用ThinkPHP通过纯真IP获取地址时,发现输出结果中文乱码,如图: 经查发现ThinkPHP的IpLocation.class.php类文件中说明:"由于使用UTF8编码 如果使用纯真IP地址库的话 需要对返回结果进行编码转换",修改浏览器编码毕竟不是长久之计, 于是尝试通过定义一个数组编码转换方法,如图: 此时修改getArea方法代码如下: 重新运行问题解决: 原文地址:https://www.cnblogs.com/splendid/p/9574642.html

使用新浪IP库获取IP详细地址

<?php class Tool{ /** * 获取IP的归属地( 新浪IP库 ) * * @param $ip String IP地址:112.65.102.16 * @return Array */ static public function getIpCity($ip) { $ip = preg_replace("/\s/","",preg_replace("/\r\n/","",$ip)); $link = &

Python读取纯真IP数据库

一.获取最新版IP地址数据库qqwry.dat 纯真IP地址数据库下载地址:http://update.cz88.net/soft/setup.zip 在windows机器上下载解压,点击setup.exe安装,在安装目录下的qqwry.dat即是最新版ip数据库. 也可从51CTO下载(不是最新版,可用于测试):http://down.51cto.com/data/1888530 二.IPLocator.py 网上找到别人用Python写的纯真IP数据库的查询程序,原文地址:http://bl

python爬虫抓取站长之家IP库,仅供练习用!

python爬虫抓取站长之家IP库,单线程的,仅供练习,IP库数据有43亿条,如果按此种方法抓取至少得数年,所以谨以此作为练手,新手代码很糙,请大家见谅. #!/usr/bin/python #coding=UTF-8 import urllib2 import re import os import csv import codecs user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' headers = { 'User-

利用淘宝IP库分析web日志来源分布

web访问日志中含有来访IP,通过IP查看归属地,最后统计访问的区域分布,可细化到省.市 淘宝接口地址:http://ip.taobao.com/service/getIpInfo.php?ip=14.215.177.38,后面的IP按需修改 例如要查看14.215.177.38这个地址的相关信息,返回的信息如下: {"code":0,"data": {"country":"\u4e2d\u56fd", "count