位操作乍一看可能不是最直观的,但一旦你明白了,你就会发现它们很容易理解。我将尝试解释此代码在字符串示例中172.16.0.1/23
的作用。netspec
第 1 部分 - CIDR 到二进制
目标是根据给定的 CIDR 前缀长度生成子网掩码的二进制表示。CIDR 前缀长度只是1
子网掩码中的一些位。第一行
final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
/
通过获取索引并解析它后面的整数来查找 CIDR 前缀长度(23
在我的示例中)。这个数字从 32 中减去得到0
子网掩码中的数字——这些位也称为主机位。
在这个例子中,我们知道我们正在处理/23
前缀,它的子网掩码应该如下所示:
n
代表网络(B 类网络为 16 位),s
代表子网,h
代表主机。对我们来说,网络位和子网位在功能上是相同的,但为了准确起见,我做了一个区分。我们的兴趣只是在主机位(它的数量)。
nnnnnnnn nnnnnnnn sssssssh hhhhhhhh
11111111 11111111 11111110 00000000
最简单的方法是使用1
s 的 32 位二进制数并用 . 填充最后 9 位0
。这是第二行出现的地方:
您可以忽略该bits == 32
检查,因为它不那么相关,并且可能只是作为一种优化。
//final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);
final int mask = 0xFFFFFFFF - ((1 << 9)-1);
0xFFFFFFFF
1
将为您提供所有s 的32 位二进制数。1
左移 9 位(1 << bits
)会给你 512 512 - 1
,二进制是111111111
:
1 << 9 10 00000000
- 1 1
--------------------------------------------------
1 11111111
当您减去这些值时,您将获得二进制子网掩码:
0xFFFFFFFF = 11111111 11111111 11111111 11111111
- (1 << 9)-1 = 1 11111111
--------------------------------------------------
11111111 11111111 11111110 00000000
这正是我们想要的网络掩码。
注意:这可能不是计算二进制值最直观的方法。我喜欢从全1的二进制数开始,而signed int中的数字的十进制值为-1
. 然后我只是将它的主机位数向左移动,就是这样。(此外,如果您正在处理大于 32 位的整数,您可以使用 0xFFFFFFFF 对其进行屏蔽):
(-1 << 9) & 0xFFFFFFFF
第 2 部分 - 二进制到点分十进制
其余代码将二进制值转换为点分十进制表示 - 255.255.254.0。
return netspec.substring(0, netspec.indexOf('/') + 1) + // part of the netspec string before '/' -> IP address
Integer.toString(mask >> 24 & 0xFF, 10) + "." + // 11111111 & 0xFF = 0xFF
Integer.toString(mask >> 16 & 0xFF, 10) + "." + // 1111111111111111 & 0xFF = 0xFF
Integer.toString(mask >> 8 & 0xFF, 10) + "." + // 111111111111111111111110 & 0xFF = 0xFE
Integer.toString(mask >> 0 & 0xFF, 10); // 11111111111111111111111000000000 & 0xFF = 0x00
return 语句由几个连接的字符串组成,从 IP 地址开始,然后是每个八位字节的十进制表示。二进制掩码右移位(4-n)*8
(其中n
是八位字节数),并使用二进制与 0xFF 你只得到最后 8 位,然后由Integer.toString
.
结果是172.16.0.1/255.255.254.0
。