網絡編程原理與UDP實現
如何發送數據包?
Q:當應用程序產生數據的時候,需要去構造數據包並發送到網絡上去,但是由誰負責處理呢?
A:現代操作系統負責數據包得構造與發送,應用程序只需提供數據。 當應用程序產生數據時,應用程序將數據交給OS內核,然後在OS內核添加各層的首部,構建好數據包,然後交給網卡,發送到網絡中去。
Q:應用程序如何向OS 發送數據呢?
A: OS為程序提供了一個接口,即socket API,類似於系統調用函數。
Q:通過socket API,只需要提供數據嗎?
A:並不,還需要告訴OS 內核,目的端口是什麼,傳輸層使用哪種協議(UDP/TCP),目的IP地址。
Q:為什麼不需要告訴源端口?
A:因為源端口是當數據進入到OS內核時,OS會為這個應用程序隨機開放一個端口,作為源端口。當服務器端收到數據,進行返回時,也從這個端口進行返回。同樣源IP地址,OS內核也是選一個網卡出去,這個網卡的地址作為源IP地址。
詳情可見下圖:
Q:加工數據是包括什麼?
A:依次包括添加傳輸層首部,網絡層首部,所以在這裡需要告訴傳輸層的目的端口,以及使用的協議,以及網絡層的目的IP地址。然後交給網卡驅動程序,構造數據鏈路層頭部,以及物理層。若想繼續詳細了解網卡的操作,可參考:網卡工作原理詳解,從網卡發送數據再談TCP/IP協議—網絡傳輸速度計算-網卡構造
了解到上述信息,知道我們要告訴OS 目的IP地址,目的端口,以及數據。
下面是使用python網絡編程客戶端的代碼:
import socket
ip="127.0.0.1"
port=8090
data = b"hello PsgQ"
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.sendto(data,(ip,port))
Q: socket函數裏面的參數是什麼意思?
A: socket.AF_INET表示使用IPv4。socket.SOCK_DGRAM表示傳輸層使用UDP。
Q:為什麼data=b”hello PsgQ”?
A: 因為在網絡中傳輸的是位元組流,b前綴將字符串轉換成bytes。對於英文字母可以這樣轉換,但對於中文字符串不行,因為b後面的字符串必須是ascii碼可以表示的。若要傳遞中文字符串,須data=bytes(’哈哈’,encoding=’UTF-8’)
具體可參考字符串和編碼
c語言版代碼隨後更新。
如何接收數據包?
網卡驅動程序收到數據,作出校驗以及檢查完相應的目的MAC地址,交到OS內核。
OS內核 網絡層模塊 進行解封裝,查看IP的目的地址,是不是發給自己的,丟棄IP地址不是自己的數據包。然後查看傳輸層使用的是什麼協議,假如是UDP,之後交給UDP模塊處理。
UDP獲得數據包後,查看目的端口,發給相應的端口。
Q:應用程序怎麼去獲得數據阿?
A:每個應用程序想要去接收數據,都需要與相應的端口進行綁定。比如HTTP,默認80端口,DNS默認使用53端口等。即OS內核交付給相應的端口,就意味着交付到某個應用程序(通過socket API)。相當於告訴內核,任何時候只要有數據到達這個端口,可以把數據交給我。具體過程是,OS 內核交給相應的端口的緩衝區,這個緩衝區是與端口進行綁定的。應用程序可從這個緩衝區讀取數據。
實際情況可能是:當沒有數據時,這個應用程序的socket API會被阻塞掉,但當數據包到達時,OS內核會去激活這個進程。
如下圖:
從上述信息了解到,我們在server端要進行綁定端口,因為可能有多個網卡,每個網卡IP地址不同,所以我們也要綁定IP地址等。
下面是使用python網絡編程服務器端的代碼:
import socket
ip="0.0.0.0"
port = 8090
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind((ip,port))
while True:
data,(ip,port)=sock.recvfrom(1024)
data=data.decode('ascii')
print("clinet:{},port:{}".format(ip,port))
print("Received:{}".format(data))
Q: ip =”0.0.0.0″?
A: 表示 數據包的目的地址可以是任意一個網卡的地址。
Q: data=data.decode(‘ascii’)
A: 因為網絡上使用的是位元組流,前面客戶端將字符串轉換成了位元組流,在這裡使用ascii碼進行解碼,使位元組流重又轉換成字符串格式。
注意:調用bind函數 ,參數是(ip,port)
最終運行結果:
c語言版代碼隨後更新。謝謝大家。