一个类似纯真IP数据库的存储和读取程序 ,删除了国外的IP数据,国内只到省市一级 如:192.168.1.2 ,192.168.2.155 福建省 厦门市
write
<?php
error_reporting(E_ALL);
set_time_limit(0); //设置脚本运行时间,参数0为无限时
$link = mysql_connect('localhost','root','1');
mysql_select_db('ip',$link);
$fd = fopen("my.hb2008", "wb");
$datahead =pack('V',8);
$dataheadend =pack('V',465824);
fwrite($fd,$datahead);
fwrite($fd,$dataheadend);
$f=0;
$k =8; //索引区偏移
$i = 465830; 记录区偏移
mysql_query("set names utf8");
$result = mysql_query("select * from wry order by fip ");
while ($row =mysql_fetch_object($result)) {
fseek($fd,$k);
$begin = $row->fip;
$endip = $row->eip;
$country = $row->country;
$area = $row->area;
$v_address = $country.' '.$area;
$length = strlen($v_address);
$v_begin = pack('V',floatval($begin));
$v_endip = pack('V',floatval($endip));
$v_offset = pack('V',floatval($i));
$v_length = pack('c',$length); //13字节
fwrite($fd,$v_begin);
fwrite($fd,$v_endip);
fwrite($fd,$v_offset);
fwrite($fd,$v_length);
fseek($fd,$i);
$f++;
fwrite($fd,$v_address);
$i = $i +$length;
$k = $k +13;
}
fclose($fd);
die('成功'.$f);
?>
read.php
<?php
/* $fp = fopen("a.hb2008", "r");
fseek($fp,80);
$data1 = fread($fp,4);
$p_data1 = unpack('Vbegin',$data1);
var_export($p_data1) ;
// $p_data2 = unpack('V',$data2);
//$count = ($p_data2 - $data1)/12;
//echo $count;
*/
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
$time_start = microtime_float();
class IpLocation {
/**
* QQWry.Dat文件指针
*
* @var resource
*/
var $fp;
/**
* ip数据文件地址
*
*@var string
*/
var $filename="IP/my.hb2008";
/**
* 第一条IP记录的偏移地址
*
* @var int
*/
var $firstip;
/**
* 最后一条IP记录的偏移地址
*
* @var int
*/
var $lastip;
/**
* IP记录的总条数(不包含版本信息记录)
*
* @var int
*/
var $totalip;
/**
* 返回读取的长整型数
*
* @access private
* @return int
*/
public function getlong() {
//将读取的little-endian编码的4个字节转化为长整型数
$result = unpack('Vlong', fread($this->fp, 4));
return $result['long'];
}
/**
* 返回压缩后可进行比较的IP地址
*
* @access private
* @param string $ip
* @return string
*/
public function packip($ip) {
// 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
// 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
return pack('N', intval(ip2long($ip))); //TCP/IP各层协议将字节序定义为Big-Endian
}
/**
* 根据所给 IP 地址或域名返回所在地区信息
*
* @access public
* @param string $ip
* @return array
*/
public function getlocation($ip) {
if (!$this->fp) {
return false; // 如果数据文件没有被正确打开,则直接返回空
}
$location['ip'] = gethostbyname($ip); // 将输入的域名转化为IP地址
$ip = $this->packip($location['ip']); // 将输入的IP地址转化为可比较的IP地址
// 不合法的IP地址会被转化为255.255.255.255 big-endian
$beginip_validate = strrev(fread($this->fp, 4)) ;
if ($ip <$beginip_validate ) {
return false;
}else {
fseek($this->fp,$this->firstip);
}
// 二分查找法
$begin = 0; // 搜索的上边界
$end = $this->totalip; // 搜索的下边界
if (!($findip = $this->binary_classification($begin,$end,$ip))) {
return false;
}
fseek($this->fp, $findip);
$begin_ip_block = $this->getlong();
$end_ip_block = $this->getlong();
$address = $this->getlong();
$pack_length = fread($this->fp,1);
$length = unpack('c',$pack_length);
fseek($this->fp,$address);
$adr =fread($this->fp,$length[1]);
//获取查找到的IP地址位置信息
$location ['beginip'] = long2ip($begin_ip_block);
$location ['endip'] = long2ip($end_ip_block);
$location ['address'] = $adr;
return $location;
}
/**
* 构造函数,打开 文件并初始化类中的信息
*
* @param string $filename
* @return IpLocation
*/
public function __construct($file = null) {
if($file) {
$this->filename = $file;
} else{
$this->filename = dirname(__FILE__)."/".$this->filename;
}
if (($this->fp = @fopen($this->filename, 'rb')) !== false) {
$this->firstip = $this->getlong();
$this->lastip = $this->getlong();
$this->totalip = (($this->lastip - $this->firstip) / 13) ;
}
else
{
echo "false";
}
}
/**
* 析构函数,用于在页面执行结束后自动关闭打开的文件。
*
*/
public function __destruct () {
if ($this->fp) {
fclose($this->fp);
}
$this->fp = null;
}
/**
* 二分法
*
*/
public function binary_classification ($begin,$end,$ip)
{
$findip = null;
while ($begin <= $end) { // 当下边界小于上边界时,查找失败
$middle = floor(($begin + $end) / 2); // 计算近似中间记录
fseek($this->fp, $this->firstip + ($middle-1) * 13);
$beginip = strrev(fread($this->fp, 4)); // 获取中间记录的开始IP地址 =>变成大端
// strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式big-endian(大端) 以便用于比较,后面相同。
if ($ip < $beginip) { // 用户的IP小于中间记录的开始IP地址时
$end = $middle - 1; // 将搜索的下边界修改为中间记录减一
}else {
$endip = strrev(fread($this->fp, 4)); // 获取中间记录的结束IP地址
if ($ip > $endip) { // 用户的IP大于中间记录的结束IP地址时
$begin = $middle + 1; // 将搜索的上边界修改为中间记录加一
}else { // 用户的IP在中间记录的IP范围内时
$findip = $this->firstip + $middle * 13; //var_dump($findip);
return $findip;
break; // 则表示找到结果,退出循环
}
}
}
}
}
var_dump( long2ip(974170117));;
$a = new IpLocation;
$b =$a->getlocation("58.16.168.5");
var_export($b);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did nothing in $time seconds/n";
?>