珊瑚虫IP库浅析
作者:bea
这不是什么新鲜事情了,很早之前就已经有人做出来了。就是使用PHP操作纯真IP库或珊瑚虫IP库,根据来访者的IP得到所在的物理位置。 我先帖出代码。然后再慢慢一步步浅析出来。希望对想了解这一块的朋友们有帮助。 Only For PHP5的代码。会继续优化代码的。 class IpLocation{ private $fp; private $wrydat; private $wrydat_version; private $ipnumber; private $firsti
这不是什么新鲜事情了,很早之前就已经有人做出来了。就是使用PHP操作纯真IP库或珊瑚虫IP库,根据来访者的IP得到所在的物理位置。
我先帖出代码。然后再慢慢一步步浅析出来。希望对想了解这一块的朋友们有帮助。
Only For PHP5的代码。会继续优化代码的。
class IpLocation{ private $fp; private $wrydat; private $wrydat_version; private $ipnumber; private $firstip; private $lastip; private $ip_range_begin; private $ip_range_end; private $country; private $area; const REDIRECT_MODE_0 = 0; const REDIRECT_MODE_1 = 1; const REDIRECT_MODE_2 = 2; function __construct(){ $args = func_get_args(); $this->wrydat = func_num_args()>0?$args[0]:'CoralWry.dat'; $this->initialize(); } function __destruct(){ fclose($this->fp); } private function initialize(){ if(file_exists($this->wrydat)) $this->fp = fopen($this->wrydat,'rb'); $this->getipnumber(); $this->getwryversion(); } public function get($str){ return $this->$str; } public function set($str,$val){ $this->$str = $val; } private function getbyte($length,$offset=null){ if(!is_null($offset)){ fseek($this->fp,$offset,SEEK_SET); } $b = fread($this->fp,$length); return $b; }/*** 把IP地址打包成二进制数据,以big endian(高位在前)格式打包* 数据存储格式为 little endian(低位在前) 如:* 00 28 C6 DA 218.198.40.0 little endian* 3F 28 C6 DA 218.198.40.0 little endian* 这样的数据无法作二分搜索查找的比较,所以必须先把获得的IP数据使用strrev转换为big endian* @param $ip* @return big endian格式的二进制数据*/ private function packip($ip){ return pack( "N", intval( ip2long( $ip))); } private function getlong($length=4, $offset=null){ $chr=null; for($c=0;$length%4!=0&&$c<(4-$length%4);$c++){ $chr .= chr(0); } $var = unpack( "Vlong", $this->getbyte($length, $offset).$chr); return $var['long']; } private function getwryversion(){ $length = preg_match("/coral/i",$this->wrydat)?26:30; $this->wrydat_version = $this->getbyte($length, $this->firstip-$length); } private function getipnumber(){ $this->firstip = $this->getlong(); $this->lastip = $this->getlong(); $this->ipnumber = ($this->lastip-$this->firstip)/7+1; } private function getstring($data="",$offset=null){ $char = $this->getbyte(1,$offset); while(ord($char) > 0){ $data .= $char; $char = $this->getbyte(1); } return $data; } private function iplocaltion($ip){ $ip = $this->packip($ip); $low = 0; $high = $this->ipnumber-1; $ipposition = $this->lastip; while($low <= $high){ $t = floor(($low+$high)/2); if($ip < strrev($this->getbyte(4,$this->firstip+$t*7))){ $high = $t - 1; } else { if($ip > strrev($this->getbyte(4,$this->getlong(3)))){ $low = $t + 1; }else{ $ipposition = $this->firstip+$t*7; break; } } } return $ipposition; } private function getarea(){ $b = $this->getbyte(1); switch(ord($b)){ case self::REDIRECT_MODE_0 : return "未知"; break; case self::REDIRECT_MODE_1: case self::REDIRECT_MODE_2: return $this->getstring("",$this->getlong(3)); break; default: return $this->getstring($b); break; } } public function getiplocation($ip){ $ippos = $this->iplocaltion($ip); $this->ip_range_begin = long2ip($this->getlong(4,$ippos)); $this->ip_range_end = long2ip($this->getlong(4,$this->getlong(3))); $b = $this->getbyte(1); switch (ord($b)){ case self::REDIRECT_MODE_1: $b = $this->getbyte(1,$this->getlong(3)); if(ord($b) == REDIRECT_MODE_2){ $countryoffset = $this->getlong(3); $this->area = $this->getarea(); $this->country = $this->getstring("",$countryoffset); }else{ $this->country = $this->getstring($b); $this->area = $this->getarea(); } break; case self::REDIRECT_MODE_2: $countryoffset = $this->getlong(3); $this->area = $this->getarea(); $this->country = $this->getstring("",$countryoffset); break; default: $this->country = $this->getstring($b); $this->area = $this->getarea(); break; } }}/* */echo microtime();echo "
";$iploca = new IpLocation;//$iploca = new IpLocation('QQWry.dat');echo $iploca->get('wrydat_version');echo "
";echo $iploca->get('ipnumber');echo "
";$iploca->getiplocation('211.44.32.34');/**/echo $iploca->get('ip_range_begin');echo "
";echo $iploca->get('ip_range_end');echo "
";echo $iploca->get('country');echo "
";echo $iploca->get('area');
echo "
";echo $iploca->get('lastip');echo "
";echo microtime();echo "
";unset($iploca);
参考资料:LumaQQ的 纯真IP数据库格式详解
CoralWry.dat文件结构上分为3个区域:
文件头[固定8个字节]
数据区[不固定长度,记录IP的地址信息]
索引区[大小由文件头决定]
该文件数据的存储方式是:little endian。在这里引用了谈谈Unicode编码里的关于little endian 与 big endian的区别
引用:
big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
文件头:红色框框里的就是文件头,前4个字节是索引区的开始地址,后4个字节是索引区的结束地址。
如下图所示:
点击放大
由于数据库是使用了little endian的字节库,所以我们需要把它倒过来。把文件头的0-3的字节读取出来,再使用 unpack 函数把二进制数据转换为big endian格式的无符号整型。处理后,索引区的开始地址位置是:00077450 ;索引区的结束地址位置是:000CE17C。如果你手头上有UltraEdit的软件,可以打开CoralWry.dat文件,查找地址为:00077450 的位置,那就是IP地址索引区的开始。如下图所示:
点击放大
红色框框住那就是索引区的开始位置。
有用 | 无用
我先帖出代码。然后再慢慢一步步浅析出来。希望对想了解这一块的朋友们有帮助。
Only For PHP5的代码。会继续优化代码的。
class IpLocation{ private $fp; private $wrydat; private $wrydat_version; private $ipnumber; private $firstip; private $lastip; private $ip_range_begin; private $ip_range_end; private $country; private $area; const REDIRECT_MODE_0 = 0; const REDIRECT_MODE_1 = 1; const REDIRECT_MODE_2 = 2; function __construct(){ $args = func_get_args(); $this->wrydat = func_num_args()>0?$args[0]:'CoralWry.dat'; $this->initialize(); } function __destruct(){ fclose($this->fp); } private function initialize(){ if(file_exists($this->wrydat)) $this->fp = fopen($this->wrydat,'rb'); $this->getipnumber(); $this->getwryversion(); } public function get($str){ return $this->$str; } public function set($str,$val){ $this->$str = $val; } private function getbyte($length,$offset=null){ if(!is_null($offset)){ fseek($this->fp,$offset,SEEK_SET); } $b = fread($this->fp,$length); return $b; }/*** 把IP地址打包成二进制数据,以big endian(高位在前)格式打包* 数据存储格式为 little endian(低位在前) 如:* 00 28 C6 DA 218.198.40.0 little endian* 3F 28 C6 DA 218.198.40.0 little endian* 这样的数据无法作二分搜索查找的比较,所以必须先把获得的IP数据使用strrev转换为big endian* @param $ip* @return big endian格式的二进制数据*/ private function packip($ip){ return pack( "N", intval( ip2long( $ip))); } private function getlong($length=4, $offset=null){ $chr=null; for($c=0;$length%4!=0&&$c<(4-$length%4);$c++){ $chr .= chr(0); } $var = unpack( "Vlong", $this->getbyte($length, $offset).$chr); return $var['long']; } private function getwryversion(){ $length = preg_match("/coral/i",$this->wrydat)?26:30; $this->wrydat_version = $this->getbyte($length, $this->firstip-$length); } private function getipnumber(){ $this->firstip = $this->getlong(); $this->lastip = $this->getlong(); $this->ipnumber = ($this->lastip-$this->firstip)/7+1; } private function getstring($data="",$offset=null){ $char = $this->getbyte(1,$offset); while(ord($char) > 0){ $data .= $char; $char = $this->getbyte(1); } return $data; } private function iplocaltion($ip){ $ip = $this->packip($ip); $low = 0; $high = $this->ipnumber-1; $ipposition = $this->lastip; while($low <= $high){ $t = floor(($low+$high)/2); if($ip < strrev($this->getbyte(4,$this->firstip+$t*7))){ $high = $t - 1; } else { if($ip > strrev($this->getbyte(4,$this->getlong(3)))){ $low = $t + 1; }else{ $ipposition = $this->firstip+$t*7; break; } } } return $ipposition; } private function getarea(){ $b = $this->getbyte(1); switch(ord($b)){ case self::REDIRECT_MODE_0 : return "未知"; break; case self::REDIRECT_MODE_1: case self::REDIRECT_MODE_2: return $this->getstring("",$this->getlong(3)); break; default: return $this->getstring($b); break; } } public function getiplocation($ip){ $ippos = $this->iplocaltion($ip); $this->ip_range_begin = long2ip($this->getlong(4,$ippos)); $this->ip_range_end = long2ip($this->getlong(4,$this->getlong(3))); $b = $this->getbyte(1); switch (ord($b)){ case self::REDIRECT_MODE_1: $b = $this->getbyte(1,$this->getlong(3)); if(ord($b) == REDIRECT_MODE_2){ $countryoffset = $this->getlong(3); $this->area = $this->getarea(); $this->country = $this->getstring("",$countryoffset); }else{ $this->country = $this->getstring($b); $this->area = $this->getarea(); } break; case self::REDIRECT_MODE_2: $countryoffset = $this->getlong(3); $this->area = $this->getarea(); $this->country = $this->getstring("",$countryoffset); break; default: $this->country = $this->getstring($b); $this->area = $this->getarea(); break; } }}/* */echo microtime();echo "
";$iploca = new IpLocation;//$iploca = new IpLocation('QQWry.dat');echo $iploca->get('wrydat_version');echo "
";echo $iploca->get('ipnumber');echo "
";$iploca->getiplocation('211.44.32.34');/**/echo $iploca->get('ip_range_begin');echo "
";echo $iploca->get('ip_range_end');echo "
";echo $iploca->get('country');echo "
";echo $iploca->get('area');
echo "
";echo $iploca->get('lastip');echo "
";echo microtime();echo "
";unset($iploca);
参考资料:LumaQQ的 纯真IP数据库格式详解
CoralWry.dat文件结构上分为3个区域:
文件头[固定8个字节]
数据区[不固定长度,记录IP的地址信息]
索引区[大小由文件头决定]
该文件数据的存储方式是:little endian。在这里引用了谈谈Unicode编码里的关于little endian 与 big endian的区别
引用:
big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
文件头:红色框框里的就是文件头,前4个字节是索引区的开始地址,后4个字节是索引区的结束地址。
如下图所示:
点击放大
由于数据库是使用了little endian的字节库,所以我们需要把它倒过来。把文件头的0-3的字节读取出来,再使用 unpack 函数把二进制数据转换为big endian格式的无符号整型。处理后,索引区的开始地址位置是:00077450 ;索引区的结束地址位置是:000CE17C。如果你手头上有UltraEdit的软件,可以打开CoralWry.dat文件,查找地址为:00077450 的位置,那就是IP地址索引区的开始。如下图所示:
点击放大
红色框框住那就是索引区的开始位置。
有用 | 无用
猜你喜欢
您可能感兴趣的文章:
- 六酷社区论坛HOME页清新格调免费版 下载
- 用PHP 快速生成 Flash 动画的方法
- 利用discuz自带通行证整合dedecms的方法以及文件下载
- 使用php重新实现PHP脚本引擎内置函数
- 15种PHP Encoder的比较
- 可定制的PHP缩略图生成程式(需要GD库支持)
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量的
- 收集的php编写大型网站问题集
- 用PHP实现Ftp用户的在线管理的代码
- 个人站长制做网页常用的php代码
- NOD32 v2.70.32 简体中文封装版 提供下载了
- 给php新手谈谈我的学习心得
- Smarty+QUICKFORM小小演示
- 实现了一个PHP5的getter/setter基类的代码
- php公用函数列表[正则]
- 发布一个用PHP fsockopen写的HTTP下载的类
- 如何提高PHP速度
- 隐藏X-Space个人空间下方版权方法隐藏X-Space个人空间标题隐藏X-Space个人空间管理版权方法
- excellent!——ASCII Art(由目标图象生成ascii)