ip_to_long

字符串 ip 转长整形原理和实现

php 内置方法:ip2long

但是这个方法存在 bug,就是 010.0.3.198 的时候,计算的长整形并非正确的长整形。并且掌握实现原理,其实并不困难。

原理:

IPv4 地址我们知道的是这样子的形式: 192.168.2.10

这种形式我们称之为 点分十进制,当然还有我们的冒分十六进制,但是这个是适用于 IPv6

回到主题,点分十进制的写法其实是一个 4 个字节,32 位的整型组成的。

例如 : 10.0.3.198

二进制形式 :00001010.00000000.00000011.11000001

说到这里,大家可能都知道了,为什么是 32 位了吧。那么二进制的 32 位转成十进制的时候,就是我们的长整形了。

转换结果为 :167773121

那么怎么转换呢???

ip 转成长整形

1
2
3
4
5
6
7
// ip转成长整形
public function ipToLong(string $ip)
{
list($position1, $position2, $position3, $position4) = explode('.', $ip);

return ($position1 << 24) | ($position2 << 16) | ($position3 << 8) | ($position4);
}

可能看到这里,你会疑惑,为什么这里面会设计位运算和逻辑运行呢。

如果还不理解为什么涉及了位运算的话,那说明上面的 32 位的 10 进制的概念还不理解。

第一段如果要按满 32 位的话,那么他还需要填补 24 位,就是 24 个 0 在右边。
第二段如果要按满 32 位的话,那么他还需要填补 24 位,分别是左边 8 位,右边 16 位。就是左边 8 个 0,右边 16 个 0。
第三段如果要按满 32 位的话,那么他还需要填补 24 位,分别是左边 16 位,右边 8 位。就是左边 16 个 0,右边 8 个 0。
第四段如果要按满 32 位的话,那么他还需要填补 24 位,就是 24 个 0 在左边。

那么为什么是逻辑运算呢,其实这里的话逻辑运行可以提高二进制的计算速度,在这里大家更倾向于逻辑运行的加法法则。
所以这里你用+号的话,括号里面的值会由 2 进制先转换程 10 进制,再进行相加,多了一步繁琐的步骤,所以大家更倾向于逻辑或的运算。

长整形转 ip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 整型转ip,三种方式,都可以,我喜欢没注释的那种
function LongToIp(int $long)
{
// return long2ip($long);

$ret = [];
for ($i = 3; $i >= 0; $i--)
{
$ret[] = ($long >> $i * 8) & 0xff;
}

// $ret[] = (($long & 0xffffffff) >> 24);
// $ret[] = (($long & 0xffffff) >> 16);
// $ret[] = (($long & 0xffff) >> 8);
// $ret[] = (($long & 0xff) >> 0);

return join('.', $ret);
}

讲到这里,转换的原理就是这样子的了,但是深入的理解,可能还要涉及到网络 ip 的知识,就是 256 进制在 ip 中的运作。