IP地址二进制转换为人类可读格式

5

我需要使用类似以下命令将IP地址存储在数据库中:

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

我的专栏是:

varbinary(16)

我尝试使用mysql函数INET6_NTOA将其转换回IPv4格式,但是没有成功。

我需要的IP地址是:

66.249.64.90

数据库的值为:

42f9405a000000000000000000000000

INET6_NTOA 给我:

42f9:405a::

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

这是我的完整查询:

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

以下是回复:

phpmyadmin output

谢谢。

(我不能只使用delete_ip,因为正如名称所示,该列将被删除。)


2
你能告诉我们为什么要使用ipV6函数来处理ipV4地址吗? - O. Jones
ip_binary 应该保存页面访问的 IP 地址的二进制表示,无论是 IPV4 还是 IPV6。手册 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html 中使用了 IPV4 和 IPV6。这是否不正确? - user3783243
1个回答

3
它没有转换回IPv4人类可读格式,而是转换为IPv6,因为INET6_NTOA的参数(二进制值)是16个字节。该函数将其视为IPv6地址的表示形式,而不是IPv4地址,后者仅为四个字节。
我认为问题可以追溯到问题中SQL的第一行,即对BINARY(16)的强制转换,它返回一个固定长度的16个字节。从返回的IPv4地址的四个字节开始,然后在右侧用零填充到16个字节的长度。
如果我们删除固定长度的转换,并允许INET6_ATON函数的结果只有四个字节会发生什么?
当数据库中存储的值只有四个字节时会发生什么?
如果我们更正stats表的内容,将16个字节的二进制值(IPv6地址的表示形式)更改为IPv4地址的四个字节的二进制表示形式,会发生什么?
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

跟进

问题描述... "使用 [expression] 将 IP 地址存储在数据库 [column] 中,如下所示:

 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字节二进制表示中哪些是实际上应该被存储为4字节的IPv4地址的方法。

如果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地址用0填充。但是我该如何通过select语句获取它呢?我需要允许16个字节,以便将IPV6地址放入此列中。 - user3783243
好的,我想我已经弄清楚了ipv4地址的问题,但是如何使它在6和4地址之间可互换?这是我的ipv4解决方案INET6_NTOA(UNHEX(SUBSTRING(CONVERT(HEX(ip_binary), CHAR(32)), 1, 8)))也许可以用一个CASE语句,如果最后X个字符都是0,但这似乎有些hacky。 - user3783243
@user3783243:不要再用SUBSTRINGHEXUNHEXCONVERTCAST等方法了……这些都是不必要的。表列是VARBINARY类型。对于IPv4地址,只需存储4个字节,对于IPv6地址,存储16个字节。让INET6_ATON函数将其转换为二进制格式。INET6_ATON返回的是VARBINARY类型。它返回4个字节的IPv4地址和16个字节的IPv6地址。这就是应该存储在表列中的内容。然后,INET6_NTOA函数可以将其从二进制格式转换回来。我的答案中添加了“跟进”信息。 - spencer7593
如果BETWEEN运算符存在问题,那么我会在实际使用BETWEEN运算符的语句中解决该问题。我会在确实需要的地方进行任何强制转换/固定长度转换。我不会为了解决可能并非实际问题的事情而改变IP地址的二进制表示。 - spencer7593
1
也许这对于处于同样情况的某人有所帮助,我因为mysql总是返回IPv6值而感到疯狂,即使使用iPv4地址,“诀窍”是将IP行数据类型从固定的BINARY(16)更改为非固定的VARBINARY(16)。 - D.Snap
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接