源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

珊瑚虫IP库浅析

  • 时间:2022-07-02 15:28 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:珊瑚虫IP库浅析
这不是什么新鲜事情了,很早之前就已经有人做出来了。 就是使用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 "\n"; $iploca = new IpLocation; //$iploca = new IpLocation('QQWry.dat'); echo $iploca->get('wrydat_version'); echo "\n"; echo $iploca->get('ipnumber'); echo "\n"; $iploca->getiplocation('211.44.32.34'); /**/ echo $iploca->get('ip_range_begin'); echo "\n"; echo $iploca->get('ip_range_end'); echo "\n"; echo $iploca->get('country'); echo "\n"; echo $iploca->get('area'); echo "\n"; echo $iploca->get('lastip'); echo "\n"; echo microtime(); echo "\n"; unset($iploca); 参考资料:LumaQQ的 纯真IP数据库格式详解 CoralWry.dat文件结构上分为3个区域: [list] [*]文件头[固定8个字节] [/*][*]数据区[不固定长度,记录IP的地址信息] [/*][*]索引区[大小由文件头决定][/*][/list]该文件数据的存储方式是:little endian。 在这里引用了谈谈Unicode编码里的关于little endian 与 big endian的区别 [b]引用[/b]: 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个字节是索引区的结束地址。 如下图所示: [url=http://www.1sucai.cn/upload/2007215172129576.jpg][img]http://files.jb51.net/upload/2007215172131920.jpg[/img] 点击放大[/url] 由于数据库是使用了little endian的字节库,所以我们需要把它倒过来。 把文件头的0-3的字节读取出来,再使用 unpack 函数把二进制数据转换为big endian格式的无符号整型。 处理后,索引区的开始地址位置是:00077450 ;索引区的结束地址位置是:000CE17C。 如果你手头上有UltraEdit的软件,可以打开CoralWry.dat文件,查找地址为:00077450 的位置,那就是IP地址索引区的开始。 如下图所示: [url=http://www.blueidea.com/articleimg/2006/12/4353/022jpg][img]http://files.jb51.net/upload/2007215172131217.jpg[/img] 点击放大[/url] 红色框框住那就是索引区的开始位置。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部