PHP解析纯真IP数据库查询IP所在地

橘子海
橘子海
发布时间:2026-10-14| 4人浏览过
原创

在Web开发中,经常需要根据用户IP判断其所在省份或城市。本文将演示如何利用PHP读取本地化的纯真IP数据库(qqwry.dat),通过高效的二分查找算法解析IP记录,获取详细的地理位置信息。

原理与准备条件

纯真IP数据库(通常命名为 qqwry.dat)采用特定的二进制结构存储IP段与地区对应关系。解析该文件需理解以下数据格式:

  • 头区域:包含第一条索引偏移量和最后一条索引偏移量,各占4字节。
  • 索引区:定长7字节,包含起始IP(4字节)、结束IP(3字节,实际通过重定向指针关联)和指向记录区的偏移量。
  • 记录区:变长,包含结束IP(4字节)和具体的地区字符串。

准备工作:你需要下载最新的纯真IP库文件,并确保文件名符合预期(默认为 qqwry.dat)。将文件放置于脚本同级目录或指定路径。

核心实现步骤

第1步:初始化IP库与文件句柄

目的:打开二进制文件,读取头部信息以确定索引范围。

复制代码 代码如下:
<?php
class iplocation {
    var $fp;          // 文件资源句柄
    var $firstip;     // 第一条ip索引的偏移地址
    var $lastip;      // 最后一条ip索引的偏移地址
    var $totalip;     // 总ip数

    function __construct($datfile = "qqwry.dat") {
        $this->fp = fopen($datfile, 'rb') or die("qqwry.dat不存在");
        $this->firstip = $this->get4b(); // 读取第一条索引偏移
        $this->lastip = $this->get4b();   // 读取最后一条索引偏移
        // 计算总IP数:索引区每条记录占7字节
        $this->totalip = ($this->lastip - $this->firstip) / 7;
    }
}

第2步:实现二进制数据读取工具方法

目的:提供读取固定长度字节、解析重定向偏移量及字符串的方法。

复制代码 代码如下:
    // 读取4个字节并转为Long整型(小端序)
    function get4b() {
        $str = unpack("v", fread($this->fp, 4));
        return $str[1];
    }

    // 读取重定向了的偏移地址(3字节+填充)
    function getoffset() {
        $str = unpack("v", fread($this->fp, 3).chr(0));
        return $str[1];
    }

    // 读取以0结尾的地区字符串
    function getstr() {
        $split = fread($this->fp, 1);
        while (ord($split) != 0) {
            $str .= $split;
            $split = fread($this->fp, 1);
        }
        return $str;
    }

    // 将IP地址转换为网络字节序(Big-Endian)用于比较
    function iptoint($ip) {
        return pack("n", intval(ip2long($ip)));
    }

第3步:二分查找定位IP区间

目的:在索引区中快速定位目标IP所在的记录行。

复制代码 代码如下:
    function searchip($ip) {
        $ip = gethostbyname($ip); // 支持域名解析
        $ip_offset["ip"] = $ip;
        $ip = $this->iptoint($ip);

        $firstip = 0;
        $lastip = $this->totalip;
        $ipoffset = $this->lastip;

        while ($firstip <= $lastip) {
            $i = floor(($firstip + $lastip) / 2);
            fseek($this->fp, $this->firstip + $i * 7); // 定位到中间记录
            
            $startip = strrev(fread($this->fp, 4)); // 读取起始IP
            if ($ip < $startip) {
                $lastip = $i - 1;
            } else {
                fseek($this->fp, $this->getoffset());
                $endip = strrev(fread($this->fp, 4)); // 读取结束IP
                if ($ip > $endip) {
                    $firstip = $i + 1;
                } else {
                    $ip_offset["offset"] = $this->firstip + $i * 7;
                    break;
                }
            }
        }
        return $ip_offset;
    }

第4步:解析地址详情(处理重定向)

目的:根据索引找到的偏移量,读取具体的省、市等信息,并处理数据库中常见的地址重定向情况(标志位1或2)。

复制代码 代码如下:
    function readaddress() {
        $now_offset = ftell($this->fp);
        $flag = $this->getflag();
        switch (ord($flag)) {
            case 0:
                $address = "";
                break;
            case 1:
            case 2:
                fseek($this->fp, $this->getoffset());
                $address = $this->getstr();
                break;
            default:
                fseek($this->fp, $now_offset);
                $address = $this->getstr();
                break;
        }
        return $address;
    }

    function getflag() {
        return fread($this->fp, 1);
    }

    function getaddress($ip) {
        $ip_offset = $this->searchip($ip);
        $ipoffset = $ip_offset["offset"];
        $address["ip"] = $ip_offset["ip"];
        
        fseek($this->fp, $ipoffset);
        $address["startip"] = long2ip($this->get4b());
        $address_offset = $this->getoffset();
        fseek($this->fp, $address_offset);
        $address["endip"] = long2ip($this->get4b());
        
        $flag = $this->getflag();
        switch (ord($flag)) {
            case 1: // 地区1地区2都重定向
                $address_offset = $this->getoffset();
                fseek($this->fp, $address_offset);
                $flag = $this->getflag();
                switch (ord($flag)) {
                    case 2: // 地区1又一次重定向
                        fseek($this->fp, $this->getoffset());
                        $address["area1"] = $this->getstr();
                        fseek($this->fp, $address_offset + 4);
                        $address["area2"] = $this->readaddress();
                        break;
                    default:
                        fseek($this->fp, $address_offset);
                        $address["area1"] = $this->getstr();
                        $address["area2"] = $this->readaddress();
                        break;
                }
                break;
            case 2: // 地区1重定向 地区2没有重定向
                $address1_offset = $this->getoffset();
                fseek($this->fp, $address1_offset);
                $address["area1"] = $this->getstr();
                fseek($this->fp, $address_offset + 8);
                $address["area2"] = $this->readaddress();
                break;
            default: // 都没有重定向
                fseek($this->fp, $address_offset + 4);
                $address["area1"] = $this->getstr();
                $address["area2"] = $this->readaddress();
                break;
        }
        
        // 过滤无用数据
        if (strpos($address["area1"], "cz88.net") !== false) {
            $address["area1"] = "未知";
        }
        if (strpos($address["area2"], "cz88.net") !== false) {
            $address["area2"] = " ";
        }
        return $address;
    }
}

调用示例与结果验证

创建实例后,直接调用 getaddress() 方法即可获取IP信息数组。

复制代码 代码如下:
$ip_obj = new iplocation("qqwry.dat");
$result = $ip_obj->getaddress("61.129.51.27");
echo '
';
print_r($result);

输出结果通常包含:ip(查询的IP)、startip(该记录的起始IP段)、endip(该记录的结束IP段)、area1(省份/国家)和 area2(城市/ISP)。

注意事项

  • 性能优化
  • 编码问题:纯真IP库通常使用GB2312编码,若你的环境是UTF-8,可能需要对返回的地区字符串进行转码(如使用 iconv 或 mb_convert_encoding)。
  • 库更新:定期更新 qqwry.dat 以保证地理位置信息的准确性。

以上就是PHP利用纯真IP数据库查询IP所在地详细内容,更多关于PHP IP查询技术的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。