AI生成的圖檔
大家都知道socket是用來在網絡中傳輸資料,是一種跨程序交換資料的常用方式。這裡将基于Python的TCP socket實作一個簡單的Server和Client的例子,作為Python socket程式設計的入門學習,同時也會讨論一些重要的注意事項。
Python socket子產品實作了基礎的socket API的接口,封裝了對作業系統級的網絡接口調用。常見的一些API如下:
Python常見的socket API
Python TCP Socket
建立socket使用如下代碼
Import socket
socket1=socket.Socket(socket.AF_INET, socket.SOCK_STREAM)
代碼中的socket.AF_INET 代表我們使用IPV4位址通信,socket.SOCK_STREAM 代表我們建立的一個TCP連接配接。TCP協定是可靠的連接配接協定,資料會按照發送的順序到達用戶端,傳輸過程中發生丢包,發送方會重新發送;而對應的UDP是不可靠的連接配接,不能保證發送的資料能全部到達接受方,也不會保證資料的先後順序。
本文例子的資料流圖
服務端和用戶端互動流程
左側是伺服器端,右側是用戶端,伺服器端建立socket後,綁定一個端口開始監聽用戶端的連接配接,用戶端使用connect連接配接後,發送資料給伺服器端,伺服器端回傳資料後,用戶端主動斷開連接配接,伺服器端收到用戶端斷開信号後斷開連接配接。
Server伺服器端
下面是server的實作
import socket
HOST = "127.0.0.1"
PORT = 12345
socket1=socket.socket(socket.AF_INET, socket.SOCK_STREAM)#建立tcp socket
socket1.bind((HOST, PORT))#綁定端口
socket1.listen(1) #開始監聽
conn, addr = socket1.accept() #阻塞等待用戶端連接配接
while True:
try:
data = conn.recv(1024)
if not data:#用戶端斷開了連接配接
conn.close()
break
conn.sendall(data)#發送資料
except Exception as e:
print(e)
下面解析一下server的代碼
socket1=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
這裡建立了TCP協定的socket ,其中socket.AF_INET代表使用IPV4位址,socket.SOCK_STREAM代表這裡建立的是TCP協定的Socket
socket1.bind((IP, PORT))
這一句用來将socket綁定在确定的主機和端口上,主機可以是IP也可以是域名。這裡是本機測試是以填寫的是127.0.0.1 ,這代表着本server隻能被本機的client通路,如果希望能接收所有位址的用戶端的通路,IP位址可以填寫空字元串或者”0.0.0.0”,如下。
socket1.bind(("", PORT)) or socket1.bind(("0.0.0.0", PORT))
PORT TCP端口的範圍是1-65535,建議使用1024以上的端口。
socket1.listen(1)
listen() 意味着伺服器端socket開始監聽接受連接配接,listen有一個可選的整數參數,代表者本伺服器最多能接收多少個連接配接,在本例中是1,代表隻能接受一個連接配接,需要注意的是Linux下能建立的Socket數量是受資源限制的。
conn, addr = socket1.accept()
accept()是一個阻塞API,等待用戶端的連接配接。當用戶端連接配接成功時這個函數會傳回一個新的socket對象和用戶端位址資訊的元組,伺服器将使用這個新的socket和和用戶端互動資料。
while True:
try:
data = conn.recv(1024)
if not data:#用戶端斷開了連接配接
conn.close()
break
conn.sendall(data)#發送資料
except Exception as e:
print(e)
成功建立連接配接收使用recv()接收用戶端發送的資料,使用sendall()發送接收到的資料。這裡接收recv()的參數buffsize這裡設定是1024,但是需要注意的是1024是代表一次能傳回的最大位元組數,不是代表一定要傳回1024個位元組。
同樣的在本例中的sendall()函數對應的另一個發送資料api是send(),他們的差別是send()不能保證一次把所有的資料都發送完成,send()函數的傳回是成功發送的位元組數,而sendall() 會持續發送資料,直到所有的資料都發送完成或者報錯退出。
如何獲知用戶端主動關閉了連接配接
if not data:
conn.close()
如果用戶端主動關閉了連接配接,recv()函數會立刻傳回空b’’ ,伺服器這時需要判斷空資料,并斷開連接配接,如本例的實作,此處是初學者容易虎忽略的地方。
Client 用戶端
用戶端的實作如下所示
import socket
PORT = 12345
socket1=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
socket1.connect(("127.0.0.1" , PORT)) #連接配接伺服器
socket1.sendall(b"Hello, world") #發送資料
data = socket1.recv(1024) #接收資料
print(data)
socket1.close() #關閉連接配接
except Exception as e:
print(e)
用戶端的實作相對簡單一些,首先建立socket 連接配接伺服器,然後發送資料并接收資料後關閉連接配接。
運作server和client
打開一個控制台視窗,先運作伺服器端
python server.py
伺服器端會阻塞在accept()處,等待連接配接
打開新的控制台視窗,然後運作用戶端
python client.py
用戶端連接配接成功後,在互動資料後用戶端會先退出,然後server端也跟着退出。
總結:
本文介紹了Python Socket的基本使用,實作了一個簡單的伺服器和用戶端的互動,此處的伺服器隻支援一個用戶端的連接配接,在後面的文章中介紹如何實作一個支援多連接配接的Python socket伺服器