首页 文章

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

提问于
浏览
4

我目前正在基于PHP的项目中使用IPv4和IPv6地址,我需要能够比较两个IP以确定哪个是更高的数字 . 例如,192.168.1.9大于192.168.1.1 . 为了做到这一点,我使用inet_ptonunpack将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

Edit :我通过将解压缩格式从"A4"(空格填充字符串)更改为"a4"(NUL填充字符串)来解决了这个问题 . 请参阅下面的答案了解详情 .

2 回答

  • 3

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

    切换到解压缩格式"a4"(NUL填充字符串)修复了该问题 . 现在唯一的IP 's that get truncated are the ones ending in .0, which is the expected behavior. I find it odd that this fixed the issue, as nearly every example I' ve用于解压缩使用格式为"A4"的IPv4和IPv6地址 . 也许他们在更新的版本中改变了空间填充 .

  • 1

    如果你想在一些有用的函数下转换为数字而不是二进制:

    function ipv6ToNum($ip)
    {
        $binaryNum = '';
        foreach (unpack('C*', inet_pton($ip)) as $byte) {
            $binaryNum .= str_pad(decbin($byte), 8, "0", STR_PAD_LEFT);
        }
        $binToInt = base_convert(ltrim($binaryNum, '0'), 2, 10);
    
        return $binToInt;
    }
    
    function ipv4ToNum($ip)
    {
        $result = 0;
        $ipNumbers = explode('.', $ip);
        for ($i=0; $i < count($ipNumbers); $i++) {
            $power = count($ipNumbers) - $i;
            $result += pow(256, $i) * $ipNumbers[$i];
        }
    
        return $result;
    }
    

    (我在ipv4ToNum中重复ip2long逻辑)

相关问题