php - 如何将 PHP 中的 IP 地址作为二进制字符串进行比较?

标签 php ipv6 ipv4 inet

我目前正在一个基于 PHP 的项目中处理 IPv4 和 IPv6 地址,我需要能够比较两个 IP 以确定哪个数字更大。例如,192.168.1.9 大于 192.168.1.1。为此,我使用 inet_pton 将 IP 转换为二进制字符串。和 unpack (我熟悉 ip2long ,但它仅限于 IPv4)。

这种方法起初似乎工作正常,但我很快发现,当我将任何以 .32 结尾的 IP 与较低的 IP 地址进行比较时,我得到了不正确的结果。例如,如果我将 192.168.1.0 与 192.168.1.32 进行比较,我的脚本会告诉我 192.168.1.0 大于 192.168.1.32。只有当其中一个 IP 以 .32 结尾时才会发生这种情况。 IP的前三个八位字节可以改变,结果是一样的。

以下 PHP 代码生成一个页面来说明此问题:

// Loop through every possible last octet, starting with zero
for ($i = 0; $i <= 255; $i++) {

    // Define two IPs, with second IP increasing on each loop by 1
    $IP1 = "192.168.1.0";
    $IP2 = "192.168.1.".$i;

    // Convert each IP to a binary string
    $IP1_bin = current(unpack("A4",inet_pton($IP1)));
    $IP2_bin = current(unpack("A4",inet_pton($IP2)));

    // Convert each IP back to human readable format, just to show they were converted properly
    $IP1_string = inet_ntop(pack("A4",$IP1_bin));
    $IP2_string = inet_ntop(pack("A4",$IP2_bin));

    // Compare each IP and echo the result
    if ($IP1_bin < $IP2_bin) {echo '<p>'.$IP1_string.' is LESS than '.$IP2_string.'</p>';}
    if ($IP1_bin === $IP2_bin) {echo '<p>'.$IP1_string.' is EQUAL to '.$IP2_string.'</p>';}
    if ($IP1_bin > $IP2_bin) {echo '<p>'.$IP1_string.' is GREATER than '.$IP2_string.'</p>';}

    // I have also tried using strcmp for the binary comparison, with the same result
    // if (strcmp($IP1_bin,$IP2_bin) < 0) {echo '<p>'.$IP1_string.' is LESS than '.$IP2_string.'</p>';}
    // if (strcmp($IP1_bin,$IP2_bin) === 0) {echo '<p>'.$IP1_string.' iS EQUAL to '.$IP2_string.'</p>';}
    // if (strcmp($IP1_bin,$IP2_bin) > 0) {echo '<p>'.$IP1_string.' is GREATER than '.$IP2_string.'</p>';}
}

?>

这是结果的示例:

192.168.1.0 is EQUAL to 192.168.1.0
192.168.1.0 is LESS than 192.168.1.1
192.168.1.0 is LESS than 192.168.1.2
192.168.1.0 is LESS than 192.168.1.3
192.168.1.0 is LESS than 192.168.1.4
...
192.168.1.0 is LESS than 192.168.1.31
192.168.1.0 is GREATER than 192.168.1.32
192.168.1.0 is LESS than 192.168.1.33
...

将 IP 转换回人类可读的格式会返回正确的 IP,因此我认为问题在于比较。我试过切换到 strcmp对于二元比较,结果是一样的。

如果您能帮助确定此问题的原因,我们将不胜感激。我不打算使用示例脚本中显示的 IP 转换和比较方法,但我确实需要坚持使用同时支持 IPv4 和 IPv6 的方法。谢谢。

我正在运行 PHP 版本 5.3.3,带有 Zend Engine v2.3.0 和 ionCube PHP Loader v4.6.1

编辑:我通过将解压格式从“A4”(空格填充字符串)更改为“a4”(NUL 填充字符串)解决了这个问题。有关详细信息,请参阅下面我的回答。

最佳答案

经过多次故障排除后,我发现了这个问题的原因。使用 unpack 时功能,我使用了用于空格填充字符串的格式代码“A4”。这导致任何以数字 32 结尾的内容都被视为空白,然后将被修剪。例如,在解压缩 192.168.1.32 的二进制值后,我将其转换为十六进制。结果为 C0A801 但本应为 C0A80120。如果 IP 是 192.32.32.32,结果会更糟,因为它会一直缩减到 C0(本质上是 192.0.0.0) .

切换到解压格式“a4”(NUL 填充字符串)解决了这个问题。现在,唯一被截断的 IP 是以 .0 结尾的 IP,这是预期的行为。我发现这解决了这个问题很奇怪,因为我遇到的几乎每个解包 IPv4 和 IPv6 地址的例子都使用格式“A4”。也许他们改变了inet_pton在较新的版本中进行空格填充。

关于php - 如何将 PHP 中的 IP 地址作为二进制字符串进行比较?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29054717/

相关文章:

PHP 运行时错误列表

获取命令行选项和参数的 PHP 库

java - 当 java.net.preferIPv4Stack=true 时,无法对 IPv6 使用 InetAddress.isReachable(timeout)

networking - 将对外部 IP 的请求重定向到本地 IP

java - 如何在服务器上获取客户端的ip?

需要 php 数组转换帮助

php - 将 MySQL 获取数组存储在变量中是否只使用一次?

hadoop - hadoop和spark现在支持ipv6了吗?

我可以 bind() 到 in6addr_any( ie::) 和 sendto() localhost

java - 强制 InetAddress.getHostAddress() 返回 IPv4 地址