在上一篇文章中咱們學習了 基於 TCP 套接字的服務端和客戶端通訊,並解決了粘包的問題,解決粘包問題的思路很簡單,就是確保接受方可以把數據收取乾淨,發多少,就收多少.python
出現粘包的問題:緣由有兩個,一個是接收數據量少於發送數據量;一個是由於爲了優化 TCP 的傳輸效率,使用了 Nagle算法,當客戶端連續發送時間間隔很短的兩個數據包時,在未確認數據發送的時候讓發送器把數據送到系統緩存裏.任何數據隨後繼續直到獲得明顯的數據確認或者直到攢到了必定數量的數據了再發包.算法
這是不可避免的,爲了優化傳輸效率,儘可能發送大塊數據,避免網絡中充斥的許多小數據塊.知道了解決方法,那麼避免粘包就很簡單了,只須要在每次發送數據以前先發送真實數據的長度,而後接收方能夠根據收到的真實長度收取數據了.編程
SOCKET AF_INET套接字主要有兩種協議,如今來看 UDP 協議是如何進行網絡通訊的.緩存
tcp 是無鏈接,不穩點的套接字,可是傳輸效率較高,因此在某些應用上如實時直播,遊戲, DNS 服務器,DHCP 服務器等等.服務器
服務端網絡
from socket import * serverSock = socket(AF_INET, SOCK_DGRAM) serverSock.bind(('', 8080)) while True: data, client_addr = serverSock.recvfrom(1024) print(data) serverSock.sendto(data.upper, client_addr) serverSock.close()
客戶端併發
from socket import * clientSock = socket(AF_INET, SOCK_DGRAM) while True: msg = input('>>>').strip() clientSock.sendto(msg.encode('utf-8'), ('', 8080)) data, server_addr = clientSock.recvfrom(1024) print(data)
當咱們連續開多個客戶端和服務端通訊的時候,沒有出現阻塞的狀況,發出去的消息均可以收回來,這是由於 udp 是無鏈接的套接字,不用關注一個鏈接,只要你給我發消息拿到了發送方的 ip 和端口,那麼就能夠直接和你通訊,並且不像 TCP 那樣必須先啓動服務端才能夠, udp 在發送數據的時候,服務端沒有啓動也能夠發送過去,只不過是發到了對方的系統緩存中.socket
那麼 udp 能夠實現多個客戶端同時和客戶端通訊嗎?tcp
以前幾個客戶端能夠同時和服務端通訊是由於服務端的處理能力很大,看起來是同時通訊同樣,可是若是把客戶端加到 1w, 甚至更多就會感受到明顯的時間差了.學習
那麼如何能夠實現真正的併發呢?關鍵點就在一個通訊循環和鏈接循環互相不干擾,不用由於 i/o 堵塞而耽擱另外一個循環要作的事.
在這裏 python 有一個模塊爲 socketserver 能夠實現真正的併發.
服務端
import sockerserver class MyTCPhandler(socketserver.BaseRequestHandler): def handler(self): while True: try: data = self.request.recv(1024) if len(data) == 0: break print('-->收到客戶端的消息:', data) self.request.send(data.upper()) except ConnectResetError: break self.request.close() if __name__ == '__main__': serverSock = socketserver.ThreadingTCPServer(('127.0.0.1', 8081), MyTCPhandler) serverSock.serve_forever() # 和客戶端進行鏈接
客戶端
from socket import * clientSock = socket(AF_INET, SOCK_STREAM) clientSock.connect(('127.0.0.1', 8081)) # 通訊循環 while True: clientSock.send(b'hello') data = clientSock.recv(1024) print(data) clientSock.close()
這樣就解決了 TCP 不能實現併發的問題了.
服務端
import socketserver class MyUDPhandler(socketserver.BaseRequestHandler): def handler(self): data, serverSock = self.request serverSock.sendto(data.upper(), self.client_address) if __name__ == '__main__': server = socketserver.ThreadingUDPServer(('127.0.0.1', 8081), MyUDPhandler) server.serve_forever()
客戶端
from socket import * clientSock = socket(AF_INET, SOCK_DGRAM) while True: clientSock.sendto(b'hello', ('127.0.0.1', 8081)) data, server_addr = client.recvfrom(1024) print(data)
這就是 UDP 實現併發的模板.
到此,網絡編程終於告一段落了,其實我寫的是網絡編程裏面極少的也是較爲重要的一部分,還有不少底層協議沒了解,像 ping 服務器時發送的 ICMP 包,還有不少,不過我以爲了解了這些其實也就瞭解了互聯網工做的基本方式,其他的待有時間了再來學習.
若有不對的地方,歡迎指正.