文档章节

PHP: 详解ip2long和long2ip

陈亦
 陈亦
发布于 2014/02/09 16:57
字数 1512
阅读 19719
收藏 57

在开发中,经常需要将IP地址转成整型进行保存,这样不仅有利于做索引,并且原本需要15个字节的存储空间,转换后只需4个字节就能存储了。但是很多人对于ip2long的结果有时候是负数并不理解,本文将详细解释这一点。因为ip2long只支持IPv4,所以本文也是基于IPv4来描述和编码的。

右移

逻辑右移

右移多少位,则在高位补多少位0。

算术右移

对无符号数做算术右移和逻辑右移的结果是相同的。但是对一个有符号数做算术右移,则右移多少位,即在高位补多少位1。

注意事项

对于C来说,只提供了>>右移运算符,究竟是逻辑右移还是算术右移这取决于编译器的行为,因此一般只提倡对无符号数进行位操作。

IPv4地址是如何表示的

IPv4使用无符号32位地址,因此最多有2的32次方减1(4294967295)个地址。一般的书写法为用4个小数点分开的十进制数,记为:A.B.C.D,比如:157.23.56.90。

IPv4地址转换成无符号整型

IPv4地址的每一个十进制数都为无符号的字节,因此范围在0~255,将IPv4地址转成无符号整型其实就是将每个十进制数放在对应的8位上组成一个4字节的无符号整型。依上图表示:157在高8位,90在低8位,23和56在中间对应的8位上。来看一个C实现的例子:

#include <stdio.h>

int main(int argc, char** argv)
{
	unsigned int ip_long = (157 << 24) | (23 << 16) | (56 << 8) | 90;
	printf("%u\n", ip_long);
	printf("%d\n", ip_long);

	return 0;
}

$ gcc -o ip2long main.c
$ ./ip2long
2635544666
-1659422630

可以看到,即使ip_long声明为无符号整型,在输出时也需要指明%u来格式化输出为无符号整型。这是因为157大于127(二进制为01111111),也就是说如果157(8位)用二进制来表示,最高位必然是1。当将157放在一个4字节整型的高8位时,导致这个4字节整型的最高位为1。虽然ip_long定义为无符号整型,但printf函数并不知道,因此需要指明无符号格式化字符。如果最高位为0,则使用%d就可以了,来看另一个例子:

#include <stdio.h>

int main(int argc, char** argv)
{
	unsigned int ip_long = (120 << 24) | (23 << 16) | (56 << 8) | 90;
	printf("%u\n", ip_long);
	printf("%d\n", ip_long);

	return 0;
}

$ gcc -o ip2long main.c
$ ./ip2long
2014787674
2014787674

PHP是如何做的

现在已经知道了为什么会出现负数。对于动态类型语言来说,数据类型一般是有符号的,所以需要我们自己转成无符号整型。PHP有内置函数ip2long来将IPv4地址转换成整型,也提供了类C的sprintf方法,因此很容易解决出现负数的问题:

<?php
echo sprintf("%u\n", ip2long("157.23.56.90"));

$ php -f test.php
2635544666

JavaScript是如何做的

JavaScript既没有提供ip2long方法,也没有提供类C的格式化函数。但JavaScript却同时提供了逻辑右移(>>>)和算术右移(>>)运算符,所以解决的方法也很简单,对结果再跟0做逻辑右移即可:

<script type="text/javascript">
console.log(((157 << 24) | (23 << 16) | (56 << 8) | 90) >>> 0);
</script>

2635544666

PHP和JavaScript的long2ip的实现

有了前面的知识,long2ip的实现就很简单了。只须从ip2long的结果中取出每8位形成的十进制数,再用点(.)连接就可以了。之前的例子都是用IP(157.23.56.90)来举例的,它的ip2long的结果是:2635544666。

PHP的long2ip的实现

<?php
$ip_long = 2635544666;
echo long2ip($ip_long) . "\n";

php -f test.php
157.23.56.90

以上代码是由PHP的内置函数long2ip来实现的。但是对于想通过移位来自己实现的童鞋来说,可能没有那么简单。因为PHP的>>运算符是算术右移运算符,所以如果最高位是1的话,右移的结果是在高位补1,这跟结果不符。但是我们可以用另一种思路去解决:保存最高位(符号位),然后将最高位置0,之后再将高8位的最高位置1(这取决于之前保存的符号位)。代码实现如下:

<?php
$ip_long = 2635544666;
// 保存最高位(符号位)
$msb = 0;
if ($ip_long & 0x80000000)
{
	$msb = 1;
}

// 将最高位(符号位)置0变成无符号数
$uip_long = $ip_long & 0x7fffffff;
$ip1 = $uip_long >> 24;
if ($msb == 1)
{
	$ip1 |= 0x80;
}

$ip2 = ($uip_long >> 16) & 0xff; // 跟0xff做与运算的目的是取低8位
$ip3 = ($uip_long >> 8) & 0xff;
$ip4 = $uip_long & 0xff;
echo $ip1 . '.' . $ip2 . '.' . $ip3 . '.' . $ip4 . "\n";

$ php -f test.php
157.23.56.90

虽然以上代码能得到正确的结果,但是并不推荐这样做。因为以上代码是假设PHP中的数据类型是32位的。这样将会有移植性问题。我们可以跟0xff做与运算来取得低8位,这样做的好处是兼容性好。代码如下:

<?php
$ip_long = 2635544666;
$ip1 = ($ip_long >> 24) & 0xff; // 跟0xff做与运算的目的是取低8位
$ip2 = ($ip_long >> 16) & 0xff;
$ip3 = ($ip_long >> 8) & 0xff;
$ip4 = $ip_long & 0xff;
echo $ip1 . '.' . $ip2 . '.' . $ip3 . '.' . $ip4 . "\n";

$ php -f test.php
157.23.56.90

另外还可以通过pack和unpack方法来实现,但要注意的是IPv4应使用大端序。

JavaScript的long2ip的实现

<script type="text/javascript">
var ip_long = 2635544666;
var ip1 = (ip_long >> 24) & 0xff;
var ip2 = (ip_long >> 16) & 0xff;
var ip3 = (ip_long >> 8) & 0xff;
var ip4 = ip_long & 0xff;
console.log(ip1 + "." + ip2 + "." + ip3 + "." + ip4);
</script>

157.23.56.90

知其然,而知其所以然。这样以后就不会为ip2long的结果是负数而感到惊讶了。

© 著作权归作者所有

陈亦
粉丝 241
博文 23
码字总数 53194
作品 0
浦东
高级程序员
私信 提问
加载中

评论(5)

fengyqf
fengyqf
楼主文章写得很细致深入,长姿势学习了。
个人观点:对于php程序员,使用ip2long(),只需知道为了可靠的输出正数,这样写就对了 (s)printf("%u\n", ip2long($ip)) 没必要自己再写转换函数。
fengyqf
fengyqf
http://www.path8.net/tn/archives/4334 这里的评论不知是否您发的,如果是请查阅我的回复。
php的64位版本下,127.x.x.x以上 ip2long()输出结果是正数
风沙
风沙
学习了,一直是把IP把补齐3位形式存为Double类型,8位,要么直接存字符串
子弹兄
子弹兄
是我没有看清楚吗?没有看到负号。不过谢谢了第一次看到这个函数!
钱途无梁
钱途无梁
写的好
PHP中IP地址与整型数字互相转换详解

IP转换成整型存储是数据库优化一大趋势,不少人目前存储IP时还在使用字符串类型存储,字符串索引比整型索引消耗资源很多,特别是表中数据量大的时候,以及求查询某一个ip段的数据,今天说的i...

mac_zhao
2014/11/08
143
0
PHP对IP地址和子网掩码的处理方法

ip2long IP地址转换成整型。 long2ip 整型数据转换成IP。 子网掩码转换成掩码长度方式: $slashnotation = strlen(pregreplace("/0/", "", decbin(ip2long($subnet_mask)))); $bits=strpos(d......

艹PHP
2014/10/31
822
0
IP转换整形(ip2long)

如何将四个字段以点分开的IP网络址协议地址转换成整数呢?PHP里有这么一个函数ip2long.比如 <?php echo ip2long("10.2.1.3"); ?> 我们将得到 167903491 这是如何计算的,目前我知道有两个算法...

net ljx
2011/04/27
255
0
DataLakeAnalytics: 解析IP地址对应的国家城市地址的能力

Data Lake Analytics 作为云上数据处理的枢纽,最近加入了通过IP地址查找对应的国家、省份、城市、ISP的函数, 今天带大家体验一下。 函数详细介绍 本次一共添加了下面这些函数: : 功能最全的...

阿里云云栖社区
02/21
26
0
DataLakeAnalytics: 解析IP地址对应的国家城市的函数 - 知乎

Data Lake Analytics 作为云上数据处理的枢纽,最近加入了通过IP地址查找对应的国家、省份、城市、ISP的函数, 今天带大家体验一下。 函数详细介绍 本次一共添加了下面这些函数: : 功能最全的...

Data Lake Analytics
04/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

rime设置为默认简体

转载 https://github.com/ModerRAS/ModerRAS.github.io/blob/master/_posts/2018-11-07-rime%E8%AE%BE%E7%BD%AE%E4%B8%BA%E9%BB%98%E8%AE%A4%E7%AE%80%E4%BD%93.md 写在开始 我的Arch Linux上......

zhenruyan
今天
5
0
简述TCP的流量控制与拥塞控制

1. TCP流量控制 流量控制就是让发送方的发送速率不要太快,要让接收方来的及接收。 原理是通过确认报文中窗口字段来控制发送方的发送速率,发送方的发送窗口大小不能超过接收方给出窗口大小。...

鏡花水月
今天
10
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
1K
11
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部