PHP 中的 INET_ATON() 和 INET_NTOA()?

Posted

技术标签:

【中文标题】PHP 中的 INET_ATON() 和 INET_NTOA()?【英文标题】:INET_ATON() and INET_NTOA() in PHP? 【发布时间】:2011-02-14 19:25:42 【问题描述】:

我想在我的数据库中存储 IP 地址,但我还需要在整个应用程序中使用它们。我阅读了有关在我的 mysql 查询中使用 INET_ATON()INET_NTOA() 从 IP 地址中获取 32 位无符号整数的信息,这正是我想要的,因为它可以比使用 char(15) 更快地搜索数据库.

问题是,我找不到在 php 中做同样事情的函数。我唯一遇到的是:

http://php.net/manual/en/function.ip2long.php

所以我测试了它:

$ip = $_SERVER['REMOTE_ADDR'];
echo ip2long($ip);

它什么也不输出。在他们给出的示例中,它似乎有效,但我又不确定ip2long() 是否与INET_ATON() 做同样的事情。

有人知道可以执行此操作的 PHP 函数吗?或者甚至是在数据库中存储 IP 地址的全新解决方案?

谢谢。

【问题讨论】:

忘记补充了,我正在使用本地主机,所以也许这就是 ip2long() 没有返回任何内容的原因? 本地主机的 IPv4 仍然是 127.0.0.1,除非您使用 IPv6,否则它应该可以工作 - 检查我的答案。 【参考方案1】:

ip2long()long2ip() 函数应该可以正常工作。

注意:您应该将它们用于 IPv4 地址——确保在您的情况下,$_SERVER['REMOTE_ADDR'] 实际上包含一个有效的 IPv4 地址(而不是一些 IPv6 的东西)

尝试使用谷歌 IP 地址:

var_dump(ip2long('209.85.227.147'));
var_dump(long2ip(3512066963));

我得到以下输出:

int(3512066963)
string(14) "209.85.227.147" 

【讨论】:

【参考方案2】:

ip2longlong2ip 和 MySQL 函数之间有一个重要区别。

PHP 的 ip2longlong2ip 处理有符号整数。

见http://php.net/manual/en/function.ip2long.php

"由于PHP的整数类型是有符号的,而且很多IP地址在32位架构上会产生负整数,所以需要使用sprintf()或printf()的'%u'格式化器来获取字符串表示未签名的 IP 地址。”

MySQL 的 INET_ATON()INET_NTOA() 处理无符号整数

见http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton

"要存储由 INET_ATON() 生成的值,请使用 INT UNSIGNED 列而不是带符号的 INT。如果使用带符号的列,则无法存储与第一个八位字节大于 127 的 IP 地址对应的值正确。”

您可以使用以下一些函数在两者之间工作。

如果您使用INET_ATON() 将IP 插入MySQL 数据库,您可以使用以下方法将其转换回PHP:

long2ip(sprintf("%d", $ip_address));

您可以使用以下方法将其转换为从 PHP 将其保存在数据库中:

sprintf("%u", ip2long($ip_address));

(同样重要的是,不要将$ip_address 类型转换为int,因为如果数字大于MAX_INT,这可能会导致问题出现问题。如果必须转换,请将其转换为@987654335 @或float)

【讨论】:

像魅力一样工作。非常感谢您的提示。我从来没有意识到这一点。 ip2long 在 64 位系统上将给出无符号结果。不过,在这种情况下,“%u”仍然会做正确的事情。【参考方案3】:

您不必在 PHP 中处理这个问题,这就是 MySQL 原生函数的用途。看这个例子:

create table iptable (
    ip int(32) unsigned not null,
    comment varchar(32) not null
);

insert into iptable (ip, comment) values (inet_aton('10.0.0.3'), 'This is 10.0.0.3');

select * from iptable;

+-----------+------------------+
| ip        | comment          |
+-----------+------------------+
| 167772163 | This is 10.0.0.3 |
+-----------+------------------+

select inet_ntoa(ip) as ip, comment from iptable;

+----------+------------------+
| ip       | comment          |
+----------+------------------+
| 10.0.0.3 | This is 10.0.0.3 |
+----------+------------------+

如果你想在同一个字段中同时处理 ipv4 和 ipv6,并且你使用的是 Mysql 5.6 或更高版本,你可以使用 varbinary(16) 以及函数 inet6_aton 和 inet6_ntoa。这是一个更好的例子,说明为什么应该使用 MySQL 函数而不是在 PHP 中处理二进制数据:

create table iptable2 (
    ip varbinary(16) not null,
    comment varchar(32) not null
);

insert into iptable2 (ip, comment) values
    (inet6_aton('192.168.1.254'), 'This is router 192.168.1.254'),
    (inet6_aton('::1'), 'This is ipv6 localhost ::1'),
    (inet6_aton('FE80:0000:0000:0000:0202:B3FF:FE1E:8329'), 'This is some large ipv6 example')
;

select * from iptable2;
+------------------+---------------------------------+
| ip               | comment                         |
+------------------+---------------------------------+
| +¿?¦             | This is router 192.168.1.254    |
|                ? | This is ipv6 localhost ::1      |
| ¦Ç      ??¦ ¦?â) | This is some large ipv6 example |
+------------------+---------------------------------+

select inet6_ntoa(ip) as ip, comment from iptable2;
+--------------------------+---------------------------------+
| ip                       | comment                         |
+--------------------------+---------------------------------+
| 192.168.1.254            | This is router 192.168.1.254    |
| ::1                      | This is ipv6 localhost ::1      |
| fe80::202:b3ff:fe1e:8329 | This is some large ipv6 example |
+--------------------------+---------------------------------+

您可以看到,通过这样做,您实际上可以避免评估不同格式的 ipv6 地址,因为 MySQL 会将它们转换为二进制并返回到最简单的表达式。

我知道这个问题已经有 2 年多了,但我想让这些信息对遇到的其他人有用。

HTH

弗朗西斯科·萨拉博佐

【讨论】:

如果你必须用准备好的语句插入它们怎么办?不能给mysql函数绑定参数,INET_ATON(?),所以必须用php @the_nuts 你在说什么?是的你可以。自己试试吧。【参考方案4】:

对于 IPv4 和 IPv6 支持,请使用 inet_pton()inet_ntop(),它们自 PHP 5.1+ 起可用,并且完全模仿等效的 MySQL 函数。

否则只使用ip2long()long2ip()

【讨论】:

我无法模仿这个。 inet_ntop( $ip ) 正是我在 PHP 中将 varbinary 值(来自 MYSQL)转换为可读 IP 地址所需的。谢谢。【参考方案5】:

这里是 PHP 替代函数(在您的程序中简单复制/粘贴) -

function inet_aton($ip)

    $ip = trim($ip);
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
    return sprintf("%u", ip2long($ip));  



function inet_ntoa($num)

    $num = trim($num);
    if ($num == "0") return "0.0.0.0";
    return long2ip(-(4294967295 - ($num - 1))); 

【讨论】:

【参考方案6】:

ip2long 等价于 inet_aton()。

ip2long 仅适用于 IPv4。我怀疑您的系统正在使用 IPv6 进行环回。尝试打印 REMOTE_ADDR。

【讨论】:

啊,我明白为什么它现在不起作用了,$_SERVER['REMOTE_ADDR'] 返回:::1。可能是因为我在本地主机上。有没有办法防止这种情况?还是我必须检查它是否返回::1 ::1 是用于环回的 IPv6。您需要禁用 IPv6。对于 Linux,请遵循以下说明:blog.taragana.com/index.php/archive/…

以上是关于PHP 中的 INET_ATON() 和 INET_NTOA()?的主要内容,如果未能解决你的问题,请参考以下文章

usleep() 和 inet_aton() 定义的奇怪行为

相当于 inet_aton 的 windows

由 Python2 和 Python3 中 socket.inet_aton() 实现不同引发的血案

《深入理解计算机系统》关于csapp.h和csapp.c的编译问题(转)

ip转十进制(PHPMySQL)

网络编程函数inet_pton()