珊瑚虫IP库浅析

5年以前  |  阅读数:734 次  |  编程语言:PHP 

这不是什么新鲜事情了,很早之前就已经有人做出来了。
就是使用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+$t7))){
    $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);

 相关文章:
PHP分页显示制作详细讲解
SSH 登录失败:Host key verification failed
获取IMSI
将二进制数据转为16进制以便显示
获取IMEI
文件下载
贪吃蛇
双位运算符
PHP自定义函数获取搜索引擎来源关键字的方法
Java生成UUID
发送邮件
年的日历图
提取后缀名
在Zeus Web Server中安装PHP语言支持
让你成为最历害的git提交人
Yii2汉字转拼音类的实例代码
再谈PHP中单双引号的区别详解
指定应用ID以获取对应的应用名称
Python 2与Python 3版本和编码的对比
php封装的page分页类完整实例