【问题标题】:IP Address Binary to Human ReadableIP 地址二进制到人类可读
【发布时间】:2018-09-18 04:40:08
【问题描述】:

我使用如下命令将 IP 地址存储在数据库中:

cast(INET6_ATON(trim(:ipbinary)) as binary(16)))

我的专栏是:

varbinary(16)

我尝试使用 mysql 函数 INET6_NTOA 将其转换回 IPv4 格式,但没有任何运气。

我需要的IP是:

66.249.64.90

DB值为:

42f9405a000000000000000000000000

INET6_NTOA 给了我:

42f9:405a::

并且INET6_NTOA(UNHEX( 给了我一个NULL 响应。我使用 PHP 作为我的脚本语言,所以如果那里有一个函数,我也可以使用它。

这是我的完整查询:

SELECT delete_ip, INET6_NTOA(ip_binary), ip_binary FROM `stats`

下面是回复:

谢谢。

(我不能只使用delete_ip,因为顾名思义,该列将被删除。)

【问题讨论】:

标签: mysql binary ipv6 ipv4


【解决方案1】:

它没有转换回 IPv4 人类可读格式,而是转换为 IPv6,因为 INET6_NTOA 的参数(二进制值)是 16 个字节。

该函数将其视为 IPv6 地址的表示,而不是 IPv4 地址,它只有四个字节。


我认为问题可以追溯到问题中的第一行 SQL,即转换为 BINARY(16)。它返回 16 个字节的 固定长度。从为 IPv4 地址返回的四个字节开始,然后在右侧用零填充,最长为 16 个字节。


如果我们移除强制转换为固定长度,并允许INET6_ATON 函数的结果只有四个字节会发生什么?

当存储在数据库中的值只有四个字节时会发生什么?

如果我们更正stats 表的内容,将那个 16 字节的二进制值(IPv6 地址的表示)更改为 IPv4 地址的 4 字节的二进制表示

UPDATE `stats` 
   SET ip_binary = INET6_ATON('66.249.64.90')
 WHERE ip_binary = CAST(INET6_ATON('66.249.64.90') AS BINARY(16))

--或者--

UPDATE `stats` 
   SET ip_binary = X'42f9405a' + 0 
 WHERE ip_binary = X'42f9405a000000000000000000000000' + 0

跟进

问题说...“使用 [表达式] 在数据库 [列] 中存储 IP 地址,如下所示:

 cast(INET6_ATON(trim(:ipbinary)) as binary(16)))

我们不需要使用CAST。而且我们不需要使用CONVERTHEX/UNHEXSUBSTR。用相同的表达式转换 IPv4 和 IPv6 地址:

  INSERT ... ip_binary ... VALUES ( ... , INET6_ATON( :ip_string ) , ...

并将它们转换回这样的字符串:

 SELECT ... , INET6_NTOA( ip_binary ) AS ip_string , ... 

CASTCONVERTSUBSTRHEX/UNHEX 的繁琐令人困惑,并导致无法正常工作。


为了更正已经存储在数据库中的值,我们需要一种方法来区分哪些 16 字节二进制表示实际上是 IPv4 地址,应该存储为 4 字节。

如果ip_delete包含字符串表示,我们可以重新转换为二进制表示。

 UPDATE `stats`
    SET ip_binary = INET6_ATON( ip_delete ) 

演示

CREATE TABLE `addr` (ip_string VARCHAR(45), ip_binary VARBINARY(16)) ;

INSERT INTO `addr` VALUES ( '66.249.64.90'         , INET6_ATON( '66.249.64.90'         ));
INSERT INTO `addr` VALUES ( '127.0.0.1'            , INET6_ATON( '127.0.0.1'            ));
INSERT INTO `addr` VALUES ( '192.168.1.1'          , INET6_ATON( '192.168.0.1'          ));
INSERT INTO `addr` VALUES ( '2001:4860:4860::8888' , INET6_ATON( '2001:4860:4860::8888' ));

SELECT ip_string, HEX(ip_binary), INET6_NTOA(ip_binary) FROM `addr` ;

ip_string             HEX(ip_binary)                    INET6_NTOA(ip_binary)
--------------------  --------------------------------  -----------------------
66.249.64.90          42F9405A                          66.249.64.90
127.0.0.1             7F000001                          127.0.0.1
192.168.1.1           C0A80001                          192.168.0.1
2001:4860:4860::8888  20014860486000000000000000008888  2001:4860:4860::8888

【讨论】:

  • 是的,你是对的。 IPV4 地址用0s` 填充。我如何通过选择取回它?我需要允许 16 个字节,以便 IPV6 IP 也可以进入此列。
  • 好的,我想我已经知道了 ipv4 地址,但我怎样才能让它在 6 和 4 个地址之间互换呢?这是我的 ipv4 解决方案 INET6_NTOA(UNHEX(SUBSTRING( CONVERT(HEX(ip_binary), CHAR(32)), 1, 8))) 可能是 CASE 如果最后 X 字符都是 0,虽然这看起来很hacky。
  • @user3783243:用SUBSTRING,HEX,UNHEX,CONVERT,CAST 抛弃繁琐的工作......这些都不需要。表格列是VARBINARY。只需为 IPv4 地址存储 4 个字节,为 IPv6 地址存储 16 个字节。让INET6_ATON 转换为二进制。 INET6_ATON 的返回值是 VARBINARY。它为 IPv4 地址返回 4 个字节,为 IPv6 返回 16 个字节。这就是应该存储在表格列中的内容。然后INET6_NTOA 可以将其从二进制转换回来。 “跟进”已添加到我的答案中。
  • 目前的方法是 Percona 推荐的。他们说Operator between: where $user_ip between ip_start and ip_end still works with binary and varbinary data. Unfortunately between operator require that number of allocated bytes will be the same. If address stored as 32bit, but mask is 128bit, false will be returned even if left bytes all zeroes. Cast from binary(4) to binary(16) adds zero bytes from the right side and concat with 12 bytes should be used instead 所以我认为我们应该保持原样,还是其中一些不正确?我们在其他地方使用between,希望不要去XY
  • 也许这对处于相同情况的人有所帮助,我快疯了,因为即使使用 iPv4 地址,mysql 也总是返回 IPv6 值,“诀窍”是将 IP 行数据类型从固定更改:BINARY( 16) 不固定:VARBINARY(16)
猜你喜欢
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
  • 2013-11-13
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 2019-05-07
相关资源
最近更新 更多