我目前正在基于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的方法.谢谢.
我正在使用Zend Engine v2.3.0和ionCube PHP Loader v4.6.1运行PHP verison 5.3.3
编辑:我通过将解包格式从“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更改为空间填充.