Discuz 5.0 中读取纯真IP数据库函数分析


Posted in PHP onMarch 16, 2007

Discuz  5.0 不在使用自己的IP数据,而是使用纯真IP的数据格式, 存取纯真IP数据库稍微有点麻烦,它的存储格式比较特殊也很有趣,具体的格式分析参考下面两个链接,其他语言实现参考文章末的链接。

《纯真IP数据库格式详解》
链接一:http://blog.csdn.net/heiyeshuwu/archive/2006/05/12/725675.aspx
链接二:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html

纯真IP数据库官网:http://www.cz88.net/ip/
纯真IP数据库下载:http://update.cz88.net/soft/qqwry.rar

以下函数conrvertip()位于 Discuz!5_GBK/upload/include/misc.func.php 路径中,有兴趣可以具体去阅读分析。(下面代码我做了简单的修改,更便于阅读,核心没有修改)

<?
//===================================
//
// 功能:IP地址获取真实地址函数
// 参数:$ip - IP地址
// 作者:[Discuz!] (C) Comsenz Inc.
//
//===================================
function convertip($ip) {
   //IP数据文件路径
   $dat_path = 'QQWry.Dat';

   //检查IP地址
   if(!preg_match("/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/", $ip)) {
       return 'IP Address Error';
   }
   //打开IP数据文件
   if(!$fd = @fopen($dat_path, 'rb')){
       return 'IP date file not exists or access denied';
   }

   //分解IP进行运算,得出整形数
   $ip = explode('.', $ip);
   $ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];

   //获取IP数据索引开始和结束位置
   $DataBegin = fread($fd, 4);
   $DataEnd = fread($fd, 4);
   $ipbegin = implode('', unpack('L', $DataBegin));
   if($ipbegin < 0) $ipbegin += pow(2, 32);
   $ipend = implode('', unpack('L', $DataEnd));
   if($ipend < 0) $ipend += pow(2, 32);
   $ipAllNum = ($ipend - $ipbegin) / 7 + 1;

   $BeginNum = 0;
   $EndNum = $ipAllNum;

   //使用二分查找法从索引记录中搜索匹配的IP记录
   while($ip1num>$ipNum || $ip2num<$ipNum) {
       $Middle= intval(($EndNum + $BeginNum) / 2);

       //偏移指针到索引位置读取4个字节
       fseek($fd, $ipbegin + 7 * $Middle);
       $ipData1 = fread($fd, 4);
       if(strlen($ipData1) < 4) {
           fclose($fd);
           return 'System Error';
       }
       //提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
       $ip1num = implode('', unpack('L', $ipData1));
       if($ip1num < 0) $ip1num += pow(2, 32);

       //提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
       if($ip1num > $ipNum) {
           $EndNum = $Middle;
           continue;
       }

       //取完上一个索引后取下一个索引
       $DataSeek = fread($fd, 3);
       if(strlen($DataSeek) < 3) {
           fclose($fd);
           return 'System Error';
       }
       $DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
       fseek($fd, $DataSeek);
       $ipData2 = fread($fd, 4);
       if(strlen($ipData2) < 4) {
           fclose($fd);
           return 'System Error';
       }
       $ip2num = implode('', unpack('L', $ipData2));
       if($ip2num < 0) $ip2num += pow(2, 32);

       //没找到提示未知
       if($ip2num < $ipNum) {
           if($Middle == $BeginNum) {
               fclose($fd);
               return 'Unknown';
           }
           $BeginNum = $Middle;
       }
   }

   //下面的代码读晕了,没读明白,有兴趣的慢慢读
   $ipFlag = fread($fd, 1);
   if($ipFlag == chr(1)) {
       $ipSeek = fread($fd, 3);
       if(strlen($ipSeek) < 3) {
           fclose($fd);
           return 'System Error';
       }
       $ipSeek = implode('', unpack('L', $ipSeek.chr(0)));
       fseek($fd, $ipSeek);
       $ipFlag = fread($fd, 1);
   }

   if($ipFlag == chr(2)) {
       $AddrSeek = fread($fd, 3);
       if(strlen($AddrSeek) < 3) {
           fclose($fd);
           return 'System Error';
       }
       $ipFlag = fread($fd, 1);
       if($ipFlag == chr(2)) {
           $AddrSeek2 = fread($fd, 3);
           if(strlen($AddrSeek2) < 3) {
               fclose($fd);
               return 'System Error';
           }
           $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
           fseek($fd, $AddrSeek2);
       } else {
           fseek($fd, -1, SEEK_CUR);
       }

       while(($char = fread($fd, 1)) != chr(0))
           $ipAddr2 .= $char;

       $AddrSeek = implode('', unpack('L', $AddrSeek.chr(0)));
       fseek($fd, $AddrSeek);

       while(($char = fread($fd, 1)) != chr(0))
           $ipAddr1 .= $char;
   } else {
       fseek($fd, -1, SEEK_CUR);
       while(($char = fread($fd, 1)) != chr(0))
           $ipAddr1 .= $char;

       $ipFlag = fread($fd, 1);
       if($ipFlag == chr(2)) {
           $AddrSeek2 = fread($fd, 3);
           if(strlen($AddrSeek2) < 3) {
               fclose($fd);
               return 'System Error';
           }
           $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
           fseek($fd, $AddrSeek2);
       } else {
           fseek($fd, -1, SEEK_CUR);
       }
       while(($char = fread($fd, 1)) != chr(0)){
           $ipAddr2 .= $char;
       }
   }
   fclose($fd);

   //最后做相应的替换操作后返回结果
   if(preg_match('/http/i', $ipAddr2)) {
       $ipAddr2 = '';
   }
   $ipaddr = "$ipAddr1 $ipAddr2";
   $ipaddr = preg_replace('/CZ88.NET/is', '', $ipaddr);
   $ipaddr = preg_replace('/^s*/is', '', $ipaddr);
   $ipaddr = preg_replace('/s*$/is', '', $ipaddr);
   if(preg_match('/http/i', $ipaddr) || $ipaddr == '') {
       $ipaddr = 'Unknown';
   }

   return $ipaddr;
}

//========================
//
//  调用举例(速度很快)
//
//========================

echo convertip('219.238.235.10');
//输出: 北京市 电信通

echo convertip('23.56.82.12');
//输出:IANA

echo convertip('250.69.52.0');
//输出:IANA保留地址

echo convertip('238.69.52.0');
//输出:IANA保留地址 用于多点传送

echo convertip('192.168.0.1');
//输出:局域网 对方和您在同一内部网

echo convertip('255.255.255.255');
//输出:纯真网络 2006年11月20日IP数据

?>

PHP 相关文章推荐
PHP 上传文件大小限制
Jul 05 PHP
PHP操作mysql函数详解,mysql和php交互函数
May 19 PHP
让Nginx支持ThinkPHP的URL重写和PATHINFO的方法分享
Aug 08 PHP
php 短链接算法收集与分析
Dec 30 PHP
PHP 基于文件头的文件类型验证类函数
May 01 PHP
怎么在Windows系统中搭建php环境
Aug 31 PHP
php根据操作系统转换文件名大小写的方法
Feb 24 PHP
php修改文件上传限制方法汇总
Apr 07 PHP
PHP+MYSQL中文乱码问题
Jul 01 PHP
php中二维数组排序问题方法详解
Aug 28 PHP
php加速缓存器opcache,apc,xcache,eAccelerator原理与配置方法实例分析
Mar 02 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
Feb 27 PHP
初级的用php写的采集程序
Mar 16 #PHP
php下使用无限生命期Session的方法
Mar 16 #PHP
随时给自己贴的图片加文字的php水印
Mar 16 #PHP
php的一个登录的类 [推荐]
Mar 16 #PHP
对Session和Cookie的区分与解释
Mar 16 #PHP
PHP中cookies使用指南
Mar 16 #PHP
PHP学习资料汇总与网址
Mar 16 #PHP
You might like
PHP4之COOKIE支持详解
2006/10/09 PHP
php求两个目录的相对路径示例(php获取相对路径)
2014/03/27 PHP
PHP获取当前页面URL函数实例
2014/10/22 PHP
PHP中通过getopt解析GNU C风格命令行选项
2019/11/18 PHP
PHP数组array类常见操作示例
2020/05/15 PHP
总结一些js自定义的函数
2006/08/05 Javascript
JavaScript 入门·JavaScript 具有全范围的运算符
2007/10/01 Javascript
javascript上传图片前预览图片兼容大多数浏览器
2013/10/25 Javascript
jQuery验证插件 Validate详解
2014/11/20 Javascript
解决angular的$http.post()提交数据时后台接收不到参数值问题的方法
2015/12/10 Javascript
通过修改360抢票的刷新频率和突破8车次限制实现方法
2017/01/04 Javascript
详解基于Node.js的微信JS-SDK后端接口实现代码
2017/07/15 Javascript
Angular实现的table表格排序功能完整示例
2017/12/22 Javascript
vue 路由页面之间实现用手指进行滑动的方法
2018/02/23 Javascript
原生JS封装_new函数实现new关键字的功能
2018/08/12 Javascript
微信小程序搭建(mpvue+mpvue-weui+fly.js)的详细步骤
2018/09/18 Javascript
Three.js实现简单3D房间布局
2018/12/30 Javascript
使用pm2部署node生产环境的方法步骤
2019/03/09 Javascript
[03:02]生活中的Dendi之野外度假篇
2016/08/09 DOTA
python实现的一个火车票转让信息采集器
2014/07/09 Python
python输出指定月份日历的方法
2015/04/23 Python
Python3处理文件中每个词的方法
2015/05/22 Python
如何处理Python3.4 使用pymssql 乱码问题
2016/01/08 Python
详解Django配置优化方法
2019/11/18 Python
Python 获取numpy.array索引值的实例
2019/12/06 Python
Python实现石头剪刀布游戏
2021/01/20 Python
HTML5获取当前地理位置并在百度地图上展示的实例
2020/07/10 HTML / CSS
什么时候需要进行强制类型转换
2016/09/03 面试题
餐饮业创业计划书范文
2014/01/06 职场文书
小学校长汇报材料
2014/08/20 职场文书
家庭困难证明
2014/10/12 职场文书
2014年社区卫生工作总结
2014/12/18 职场文书
2015年安全月活动总结
2015/03/26 职场文书
离婚案件答辩状
2015/05/22 职场文书
同学会感言
2015/07/30 职场文书
幼儿园师德师风心得体会
2016/01/12 职场文书