首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何将IPv6从二进制转换为存储在MySQL中

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

Stack Overflow用户
提问于 2009-07-13 16:00:44
回答 3查看 16.8K关注 0票数 16

我正在尝试以一种高效的方式在MySQL 5.0中存储IPv6地址。我已经阅读了与此相关的其他问题,such as this one。这个问题的作者最终选择了两个BIGINT字段。我的搜索还发现了另一种常用的机制:使用十进制(39,0)来存储IPv6地址。我对此有两个问题。

  1. 与其他方法(如2*BIGINT)相比,使用DECIMAL(39,0)的优点和缺点是什么?
  2. 如何将inet_pton()返回的二进制格式转换为MySQL可用的十进制字符串格式,以及如何转换回来以便可以使用inet_ntop()进行美观打印?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-08-13 10:11:44

下面是我现在用来将IP地址从十进制(39,0)格式转换为十进制(39,0)格式的函数。它们被命名为inet_ptod和inet_dtop,表示“表示到小数”和“小数到表示”。它需要PHP中的IPv6和bcmath支持。

代码语言:javascript
复制
/**
 * 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;
}
票数 19
EN

Stack Overflow用户

发布于 2009-10-25 06:18:14

我们改为使用VARBINARY(16)列,并使用inet_pton()inet_ntop()进行转换:

https://github.com/skion/mysql-udf-ipv6

这些函数可以加载到正在运行的MySQL服务器中,并将为您提供SQL语言中的INET6_NTOPINET6_PTON,就像IPv4中熟悉的INET_NTOAINET_ATON函数一样。

编辑:现在只有different names,MySQL中有兼容的函数。仅当您使用5.6版之前的MySQL并且正在寻找方便的未来升级途径时,才能使用上述选项。

票数 31
EN

Stack Overflow用户

发布于 2018-03-04 11:51:34

十进制(39)

优点:

  • 使用基本算术运算符(如+和-)。
  • 使用基本索引(精确或范围)。
  • 格式显示友好。

缺点:

  • 可以接受IPv6的超出范围的值。
  • 不是一个非常有效的存储mechanism.
  • Can,这会造成混淆,无法确定哪些数学运算符或函数有效,哪些无效。

BINARY(16)...

优点:

对于具有基本索引的精确representation.

  • Works,它是最有效的格式(对于是8位的倍数的前缀,使用exact和range).

  • Works

  • prefix索引。

  • 仅存储有效的IPv6值(尽管不能保证在以后的版本中具有支持此格式与IPv6表示形式(但不支持4in6)相互转换的函数)。

缺点:

对于display.

  • Isn't,
  • 不友好,对运算符或数字函数不友好。

BINARY(39)...

这是针对完整地址的(即使对4in6也使用十六进制)。也可以是ascii而不是二进制。

优点:

前缀可读(如果您可以调用that).

  • Supports basic indexing (exact和range).

  • Supports

  • indexing for multiple of 4 bits.

  • Directly IPv6兼容。不需要转换。

缺点:

对于任何数学函数或operators.

  • Most低效存储,
  • 都不能很好地工作。
  • 可能允许无效的Can

奇怪之处:

如果你想要一些东西,比如case insensitive.

  • IPv6有其他的显示格式,
  • 就会变得复杂,尽管使用这些格式会带来更多的复杂性,比如你可以有两个相同地址的表示,或者你会丢失范围查找。甚至可以最终必须将其设置为45字节长,或者使用varchar/varbinary.
  • Variances可以支持将地址保留为最初接收到的地址。这可能是很少需要的,但当它丢失了大量的benefits.
  • Remove时,全格式的分隔符和只存储的分隔符是十六进制字符串,这样可以减少麻烦,提高效率。如果前缀索引很重要,您可以在很长一段时间内实现这一点( (BINARY(128)).

无符号* 2

优点:

prefix使用数学运算符和函数,但需要注意的是,当columns.

  • Efficient是64 bits.

  • Display友好格式时,必须围绕它做额外的事情,但同样需要注意的是,它是两列,将添加一些具有基本索引的索引(确切地说,当
  • 是64 bits.
  • Display友好格式时,使用前缀索引。

缺点:

  • Two columns使其成为非原子的,这意味着对它的大量操作要加倍。

奇怪之处:

  • 许多现代语言和系统都提供64位整数,但不是无符号的。签名是有问题的。负数表示为小于正数,但它们的比特序列实际上更高。由于这个原因,通常使用4* instead,人们可能会将其分解为前缀索引,并且您至少可以使用8位(TINYINT UNSIGNED.
  • Similarly )。有些人可能还会使用位(1)类型进行完整的前缀索引,假设MySQL在位类型properly.
  • Again上使用类似于四列的索引。具有讽刺意味的是,由于计算过程中有空闲的位(计算中的中间值仍然可以是64位),一些需要从一个位转到另一个位的操作会更容易。

摘要

人们会出于不同的原因使用不同的格式。向后兼容性可能是一个原因,这取决于为IPv4所做的事情。其他的则取决于地址的使用方式和优化方法。您可能会看到使用了多个方法。

B16是一种很好的默认方法,因为它是最高效、最省事的。

对于PHP中的转换,如果您研究以下内容,可以手动完成:

  • gmp或bcmath

  • PHP的数字处理和位运算符,请特别注意int或float的限制,以及依赖于它们的函数,否则这些函数在

  • IPv6 PHP中可能很有用。

不过,我建议使用一个通用库来处理IPv6的各种显示格式。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1120371

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档