如何在MySQL中将IPv6从二进制转换为存储

San*_*hal 16 php mysql ipv6

我试图以有效的方式在MySQL 5.0中存储IPv6地址.我已经阅读了与此相关的其他问题,例如本文.该问题的作者最终选择了两个BIGINT字段.我的搜索也发现了另一种常用机制:使用DECIMAL(39,0)存储IPv6地址.我有两个问题.

  1. 使用DECIMAL(39,0)比其他方法(例如2*BIGINT)有什么优缺点?
  2. 如何将(在PHP中)从inet_pton()返回的二进制格式转换为MySQL可用的十进制字符串格式,以及如何转换回来以便我可以使用inet_ntop()进行漂亮打印?

Pie*_*nes 31

我们去了一个VARBINARY(16)专栏并使用inet_pton()inet_ntop()进行转换:

https://bitbucket.org/skion/mysql-udf-ipv6

该功能可以加载到正在运行的MySQL服务器,并给你INET6_NTOPINET6_PTON在SQL,就像熟悉INET_NTOAINET_ATON功能的IPv4.

编辑:现在MySQL中有兼容的功能,只是名称不同 .如果您使用的是5.6之前的MySQL,并且正在寻找方便的未来升级路径,请仅使用上述内容.


San*_*hal 19

以下是我现在用于将IP地址转换为DECIMAL(39,0)格式的函数.它们被命名为inet_ptod和inet_dtop,用于"presentation-to-decimal"和"decimal-to-presentation".它需要PHP中的IPv6和bcmath支持.

/**
 * Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL
 *
 * @param string $ip_address An IP address in IPv4, IPv6 or decimal notation
 * @return string The IP address in decimal notation
 */
function inet_ptod($ip_address)
{
    // IPv4 address
    if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) {
        $ip_address = '::' . $ip_address;
    }

    // IPv6 address
    if (strpos($ip_address, ':') !== false) {
        $network = inet_pton($ip_address);
        $parts = unpack('N*', $network);

        foreach ($parts as &$part) {
            if ($part < 0) {
                $part = bcadd((string) $part, '4294967296');
            }

            if (!is_string($part)) {
                $part = (string) $part;
            }
        }

        $decimal = $parts[4];
        $decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
        $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
        $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));

        return $decimal;
    }

    // Decimal address
    return $ip_address;
}

/**
 * Convert an IP address from decimal format to presentation format
 *
 * @param string $decimal An IP address in IPv4, IPv6 or decimal notation
 * @return string The IP address in presentation format
 */
function inet_dtop($decimal)
{
    // IPv4 or IPv6 format
    if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) {
        return $decimal;
    }

    // Decimal format
    $parts = array();
    $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
    $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
    $parts[2] = bcdiv($decimal, '18446744073709551616', 0);
    $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
    $parts[3] = bcdiv($decimal, '4294967296', 0);
    $decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
    $parts[4] = $decimal;

    foreach ($parts as &$part) {
        if (bccomp($part, '2147483647') == 1) {
            $part = bcsub($part, '4294967296');
        }

        $part = (int) $part;
    }

    $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]);
    $ip_address = inet_ntop($network);

    // Turn IPv6 to IPv4 if it's IPv4
    if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) {
        return substr($ip_address, 2);
    }

    return $ip_address;
}
Run Code Online (Sandbox Code Playgroud)

  • 男孩.好的,那段代码中没有任意魔法数字. (22认同)
  • 79228162514264337593543950336 = 2 ^ 96 --- 18446744073709551616 = 2 ^ 64 --- 4294967296 = 2 ^ 32 ---请注意,你遗憾地不能使用这种速记符号,因为它们对于PHP来说很重要. (6认同)
  • 如果你知道你的2的力量,它们不是任意的或魔法;-) (5认同)