【发布时间】:2015-05-17 06:17:45
【问题描述】:
我目前在一个基于 PHP 的项目中使用 IPv4 和 IPv6 地址,我需要能够比较两个 IP 以确定哪一个是更大的数字。例如,192.168.1.9 大于 192.168.1.1。为此,我使用inet_pton 和unpack 将IP 转换为二进制字符串(我熟悉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 填充字符串)解决了这个问题。有关详细信息,请参阅下面的答案。
【问题讨论】:
-
欢迎使用 PHP。享受你极弱的打字。
-
但先尝试将它们转换为数字。
-
对于 IPv6,这些将是非常大的二进制数(太大而无法转换为整数)。我尝试将它们转换为二进制而不改变结果。
-
另外,strcmp 用于比较二进制字符串。如上所述,当我尝试与此方法进行比较时,结果是相同的。
-
未经测试 - 可能有用:IPvx provides useful functions for IP (v4/v6) address。似乎转换为二进制格式可能更容易比较?。