Python網路程式設計(一)
一、網路基礎
在學習網路程式設計前,要對網路通訊的五層協議有所瞭解,那什麼是協議呢,協議就是各方規定遵守的一種標準。
網路通訊就像寄信件,是資訊與資料的交換,而在生活中我們寄信件,信件也不是從我們手裡瞬間到收件人手裡,每一次信件通訊,都會經歷這樣幾個固定流程:寫信、裝信封、投到郵箱、郵局取件、運輸到目的地郵局、目的地郵局根據詳細地址派送、收件人收件、拆信封、讀信。
網路通訊也是同樣的道理,資料的傳輸總有一定的流程:傳送端程式將資料打包,給資料包印上目標地址,將資料包交給閘道器,通過路由轉發到達目的網路,目的網路閘道器在根據詳細地址分發、目的主機接收資料、拆包、讀資料。
這中間我們按照每部分負責的任務的不同,將整個通訊劃分為五層(分法不唯一,這是最易理解的一種),由下到上分別是物理層、資料鏈路層、網路層、運輸層、應用層,每一層都將較下的一層完全封裝,消除通訊雙方軟硬體的差別,而每一層又有約定好的規則以和對方通訊,我們這篇文章所要講的,就是發生在運輸層的通訊。
我們先來思考一個問題:一臺主機的一個程序傳送資料時,是如何標識它是傳送給目標主機的哪個程序呢?目標主機接受到資料時,由如何判斷這份資料是傳送給本機的哪個程序呢?
這就是運輸層的任務,也就是說,運輸層負責兩個主機中的應用程序之間的通訊。那麼上面的問題到底是怎麼解決的呢?這就要提到一個新的概念:套接字。
套接字的概念很簡單,每臺主機有一個唯一的主機地址標識,同時主機內還有標識自己程序的序號id,稱作埠,將這兩個識別符號結合就構成了一個套接字(socket),這個套接字能唯一標識網路中的一個程序。接下來我們的網路程式設計,都是圍繞這個套接字展開的。
運輸層的協議主要有UDP和TCP協議,這兩個協議都是為了解決程序通訊,它們的區別主要是這幾點:UDP是無連線、不可靠協議,TCP是有連線、可靠協議,連線指的是通訊前要建立連線(連線提供了許多功能:確認、流量控制、連線管理等),可靠指的是如果資料未抵達有對應處理(不可靠則指只負責傳送資料)。
這樣就瞭解的差不多了,當然,關於運輸層的技術還有很多,難點重點都沒有提到,有興趣的同學可以自己再學習。
二、通訊模式
對我們來說,進行網路通訊的目的是為了獲取資訊,而網際網路上的資訊需求者遠遠多於資訊提供者,不止如此,資訊的提供者必須24小時立即回覆需求者的請求,這樣就產生了客戶伺服器模式。客戶伺服器模式是一個邏輯概念,它將主機分為兩類,等待請求的是伺服器端、發起請求的是客戶端,所以伺服器端的套接字是必須是公開的,客戶端的套接字可以在請求時給伺服器端。
三、實現
對UDP來說:因為不需要建立連線,所以只需要在兩臺主機上各構造一個socket,就能直接傳送和接收資料了。
對TCP來說:首先需要建立連線,一個連線就是一對手動繫結的socket,連線成功後就能收發資料了。
結合客戶服務端模式,對同一種協議,socket的構造方法也按客戶端和伺服器分為兩種。
話不多說,上程式碼!
這是基於TCP協議的:
#socket_server_tcp # import socket from socket import * ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_server=socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: print('服務端開始運行了') conn,addr=tcp_server.accept() #服務端阻塞 print('雙向連結是',conn) print('客戶端地址',addr) while True: try: data=conn.recv(buffer_size) print('客戶端發來的訊息是',data.decode('utf-8')) conn.send(data.upper()) except Exception: break conn.close() tcp_server.close()
#socket_client_tcp # import socket from socket import * ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_client=socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: msg=input('>>: ').strip() if not msg:continue tcp_client.send(msg.encode('utf-8')) print('客戶端已經發送訊息') data=tcp_client.recv(buffer_size) print('收到服務端發來的訊息',data.decode('utf-8')) tcp_client.close()
接下來是基於UDP協議的:
# socket_server_udp from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_server=socket(AF_INET,SOCK_DGRAM) #資料報 udp_server.bind(ip_port) while True: data,addr=udp_server.recvfrom(buffer_size) print('收到來自{}的訊息:{}'.format(addr, data)) udp_server.sendto(data.upper(),addr)
# socket_client_udp from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_client=socket(AF_INET,SOCK_DGRAM) #資料報 while True: msg=input('>>: ').strip() udp_client.sendto(msg.encode('utf-8'),ip_port) data,addr=udp_client.recvfrom(buffer_size) # print(data.decode('utf-8')) print('收到服務端發來的訊息', data)
執行下試試!
這兩對程式碼已經能實現基本的功能,但還有許多功能沒有實現,如TCP版不能同時連線多使用者,UDP版有氈包問題(一次接收的資料如果大於快取區大小就會把剩下的內容附加到下次接受的開頭),這些我們放到下次來講。