今天从Python的角度来聊下计算机网络这行基础中的基础的话题:网络和IP地址计算(注:本文里的IP指的是IPv4,不涉及IPv6)。相信几乎每位网工读者在平时的工作和学习中都用过类似下图的在线网络和IP地址计算器吧:

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(1)

这类前人(或者说码农们)造出的轮子的确很好用,但是很少有网工明白它们背后的工作原理(也就是代码是怎么写出来的)。作为有志成为NetDevOps Engineer的我们有必要深入的从代码的角度来学习一下,不妨自己也用Python从零写一个交互式的网络和IP地址计算器,重新造一遍轮子,一来可以温故知新,二来可以帮助我们更深入的了解二进制和十进制在Python里是怎么玩的。该交互式计算器的作用是让用户输入一个合法的IP地址及子网掩码,然后根据用户输入的信息自动给出用户查询的网段的网络IP、广播IP、网段内可用的IP地址数、反掩码以及用户输入的子网掩码对应的“/”格式的掩码位(比如用户输入的掩码是255.255.128.0,计算器会自动在结果中给出/17的掩码位)。

因为是所有网工必须掌握的基本功,为了节约篇幅,下面我只高度概括一下网络和IP地址计算的理论要点,我们重点要关注的是如何在Python中实现它们(所有演示我都将在解释器里实时完成,让读者更清楚的看到十进制和二进制的相互转换在Python中是怎样完成的)大致可以归纳为A,B,C,D,E总共5个点,分述如下:

A.

我们知道任何一个合法的IP地址和子网掩码都可以用32位的二进制(binary)表示,这32位二进制又被分为4个八位位组(octet),比如192.168.1.1用二进制可以写成11000000.10101000.00000001.00000001,这个转换步骤在Python中实现的方法如下:

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(2)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(3)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(4)

是不是只得到了一位数的二进制数0?加上zfill(8)后即得到八位组的二进制00000000,效果如下:

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(5)

同样的代码也适用于子网掩码,比如在Python中要将255.255.255.0这个掩码转换成二进制形式,代码可以这么写:

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(6)

B.

知道如何在Python里将十进制的IP地址和子网掩码转换成二进制后,我们再来看下如何将二进制的IP地址和子网掩码转换回十进制(代码接续前文):

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(7)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(8)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(9)

C.

我们知道要算出一个网段内有多少可用的IP地址需要知道该网段的子网掩码以二进制表达时里面有多少个0 (number of zeros,在Python中我们将其赋值给变量no_of_zeros),然后套用公式2 ** no_of_zeros - 2即可算出,比如这里给定的子网掩码255.255.255.0,将其转化为二进制为1111111.1111111.11111111.00000000,总共8个0, 那么2**8-2 = 254,即为我们要的结果,这个运算过程在Python中的计算方式如下(代码接前文):

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(10)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(11)

D.

我们知道网络IP和广播IP是两个很重要的概念,在给定一个IP地址及其子网掩码后,计算该网段的网络IP和广播IP的方法想必大家都知道,即将IP地址和子网掩码分别转换成二进制,然后将两者对比,看子网掩码的二进制有多少个1,那么IP地址的二进制就从左至右保留多少位,剩下的部分全部以0填充,即可得到网络IP的二进制地址,如果剩下部分全部以1填充,则得到广播IP的二进制地址(这里就不画图演示了,这些都是网工最最最基础的知识点,不懂的回去把CCENT或CCNA的书重新翻出来读)。下面我们在Python中演示如何实现找出一个指定IP所在网段的网络IP和广播IP(代码接前文,以前文给定的IP地址192.168.1.1和子网掩码255.255.255.0为例):

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(12)

依葫芦画瓢,从下面这段代码中我们又得到了广播IP: 192.168.1.255

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(13)

E.

最后我们来谈谈反掩码,所谓反掩码就是将子网掩码的二进制里的1换成0,将0换成1,比如255.255.255.0的二进制为11111111.11111111.11111111.00000000,它的反掩码即为00000000.00000000.00000000.11111111,也就是0.0.0.255。在Python里我们可以这样表示(代码接上文):

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(14)

最后来看下该交互式的网络和IP地址计算器的最终代码:

#coding=utf-8 import sys try: while True: #判断用户输入的IP是否符合规范,如果不规范则while循环反复询问,直到用户输入正确IP地址为止。 ip_address = input("输入要查询的IP地址: ") ip_octets = ip_address.split('.') #将IP地址用split()转换成列表,该列表有4个元素,分别代表用户输入的IP地址的4个8位字段。 #0.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16以及Class D这些保留IP地址均不是有效的IP if (len(ip_octets) == 4) and (1 <= int(ip_octets[0]) <= 223) and (int(ip_octets[0]) != 127) and (int(ip_octets[0]) != 169 or int(ip_octets[1]) != 254) and (0 <= int(ip_octets[1]) <= 255 and 0 <= int(ip_octets[2]) <= 255 and 0 <= int(ip_octets[3]) <= 255): break else: print("\n不是有效的IP地址,请重新输入\n") continue masks = [255, 254, 252, 248, 240, 224, 192, 128, 0] #将所有有效的子网掩码的十进制数字归纳进一个列表,用于验证用户输入的子网掩码是否合乎规范 while True: #判断用户输入的子网掩码是否符合规范,如果不规范则while循环反复询问,直到用户输入正确子网掩码为止。 subnet_mask = input("输入子网掩码: ") mask_octets = subnet_mask.split('.') #将子网掩码用split()转换成列表,该列表有4个元素,分别代表用户输入的子网掩码的4个8位字段。 #支持/0 - /32所有子网掩码 if (len(mask_octets) == 4) and (int(mask_octets[0]) in masks) and (int(mask_octets[1]) in masks) and (int(mask_octets[2]) in masks) and (int(mask_octets[3]) in masks) and (int(mask_octets[0]) >= int(mask_octets[1]) >= int(mask_octets[2]) >= int(mask_octets[3])): break else: print("\n不是有效的子网掩码,请重新输入\n") continue mask_octets_binary = [] for octet in mask_octets: binary_octet = bin(int(octet)).lstrip('0b') #print(binary_octet) mask_octets_binary.append(binary_octet.zfill(8)) #print(mask_octets_binary) binary_mask = "".join(mask_octets_binary) #print(decimal_mask) no_of_zeros = binary_mask.count("0") no_of_ones = 32 - no_of_zeros no_of_hosts = abs(2 ** no_of_zeros - 2) #当掩码为/32时,2的0次方减1等于-1,需要用abs()函数将其转换成正数1. #print(no_of_zeros) #print(no_of_ones) #print(no_of_hosts) wildcard_octets = [] for octet in mask_octets: wild_octet = 255 - int(octet) wildcard_octets.append(str(wild_octet)) #print(wildcard_octets) wildcard_mask = ".".join(wildcard_octets) #print(wildcard_mask) ip_octets_binary = [] for octet in ip_octets: binary_octet = bin(int(octet)).lstrip('0b') #print(binary_octet) ip_octets_binary.append(binary_octet.zfill(8)) #print(ip_octets_binary) binary_ip = "".join(ip_octets_binary) #print(binary_ip) network_address_binary = binary_ip[:(no_of_ones)] "0" * no_of_zeros #print(network_address_binary) broadcast_address_binary = binary_ip[:(no_of_ones)] "1" * no_of_zeros #print(broadcast_address_binary) net_ip_octets = [] for bit in range(0, 32, 8): net_ip_octet = network_address_binary[bit: bit 8] net_ip_octets.append(net_ip_octet) #print(net_ip_octets) net_ip_address = [] for each_octet in net_ip_octets: net_ip_address.append(str(int(each_octet, 2))) #print(net_ip_address) network_address = ".".join(net_ip_address) #print(network_address) bst_ip_octets = [] for bit in range(0, 32, 8): bst_ip_octet = broadcast_address_binary[bit: bit 8] bst_ip_octets.append(bst_ip_octet) #print(bst_ip_octets) bst_ip_address = [] for each_octet in bst_ip_octets: bst_ip_address.append(str(int(each_octet, 2))) #print(bst_ip_address) broadcast_address = ".".join(bst_ip_address) #print(broadcast_address) print("\n") print("该网段的网络地址为: %s" % network_address) print("该网段的广播地址为: %s" % broadcast_address) print("该网段可用的IP地址数量为: %s" % no_of_hosts) print("反掩码: %s" % wildcard_mask) print("掩码位: %s" % no_of_ones) print("\n") print(input()) except KeyboardInterrupt: print("\n\n程序终止\n") sys.exit()

最后运行该程序看效果:

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(15)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(16)

用python编写一个计算器程序(用Python开发一个交互式网络和IP地址计算器)(17)

,