今天從Python的角度來聊下計算機網絡這行基礎中的基礎的話題:網絡和IP地址計算(注:本文裡的IP指的是IPv4,不涉及IPv6)。相信幾乎每位網工讀者在平時的工作和學習中都用過類似下圖的在線網絡和IP地址計算器吧:
這類前人(或者說碼農們)造出的輪子的确很好用,但是很少有網工明白它們背後的工作原理(也就是代碼是怎麼寫出來的)。作為有志成為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中實現的方法如下:
是不是隻得到了一位數的二進制數0?加上zfill(8)後即得到八位組的二進制00000000,效果如下:
同樣的代碼也适用于子網掩碼,比如在Python中要将255.255.255.0這個掩碼轉換成二進制形式,代碼可以這麼寫:
B.
知道如何在Python裡将十進制的IP地址和子網掩碼轉換成二進制後,我們再來看下如何将二進制的IP地址和子網掩碼轉換回十進制(代碼接續前文):
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中的計算方式如下(代碼接前文):
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為例):
依葫蘆畫瓢,從下面這段代碼中我們又得到了廣播IP: 192.168.1.255
E.
最後我們來談談反掩碼,所謂反掩碼就是将子網掩碼的二進制裡的1換成0,将0換成1,比如255.255.255.0的二進制為11111111.11111111.11111111.00000000,它的反掩碼即為00000000.00000000.00000000.11111111,也就是0.0.0.255。在Python裡我們可以這樣表示(代碼接上文):
最後來看下該交互式的網絡和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()
最後運行該程序看效果:
,
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!