python使用SocketServer實現網路伺服器
- 2019 年 10 月 5 日
- 筆記
python使用SocketServer實現網路伺服器
SocketServer簡化了網路伺服器的編寫。在進行socket創建時,使用SocketServer會大大減少創建的步驟,並且SocketServer使用了select它有4個類:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。這4個類是同步進行處理的,另外通過ForkingMixIn和ThreadingMixIn類來支援非同步。
使用SocketServer的步驟簡介
創建伺服器的步驟。首先,你必須創建一個請求處理類,它是BaseRequestHandler的子類並重載其handle()方法。
實例化一個伺服器類,傳入伺服器的地址和請求處理程式類。
最後,調用handlerequest()(一般是調用其他事件循環或者使用select())或serveforever()。
集成ThreadingMixIn類時需要處理異常關閉。daemon_threads指示伺服器是否要等待執行緒終止,要是執行緒互相獨立,必須要設置為True,默認是False。
無論用什麼網路協議,伺服器類有相同的外部方法和屬性。
測試案例
伺服器端為一個時間戳伺服器,在接收到客戶端發來的數據後,自動回復。
客戶端,等待用戶輸入,回車後向伺服器發送用戶輸入的內容。
分別在python2.7和python3.6下測試。在啟動時需要先啟動伺服器端,在啟動客戶端。
python2.7下
伺服器端程式碼為
#coding:utf-8 import SocketServer from time import ctime print("=====================SocketServer TCP伺服器====================="); HOST = '' #主機號為空白表示可以使用任何可用的地址。 PORT = 21567 #埠號 ADDR = (HOST, PORT) class MyRequestHandler(SocketServer.StreamRequestHandler): #StreamRequestHandler實現TCP/UDP伺服器的服務處理器 def handle(self): #重寫接收響應函數 print('...connect from:', self.client_address) data = self.rfile.readline().strip() print(data) self.wfile.write('[%s] %s' % (ctime(), data)) tcpSerSock = SocketServer.TCPServer(ADDR, MyRequestHandler) print('等待連接...') tcpSerSock.serve_forever()
客戶端程式碼為
#coding:utf-8 from socket import * print("=====================SocketServer TCP客戶端====================="); HOST = '127.0.0.1' #本機測試 PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) while True: tcpCliSock = socket(AF_INET, SOCK_STREAM) #創建客戶端套接字 tcpCliSock.connect(ADDR) #發起TCP連接 data = raw_input('> ') #接收用戶輸入 if not data: #如果用戶輸入為空,直接回車就會發送"",""就是代表false break tcpCliSock.send(data+'n') #客戶端發送消息,必須發送位元組數組 data = tcpCliSock.recv(BUFSIZ) #接收回應消息,接收到的是位元組數組 if not data: #如果接收伺服器資訊失敗,或響應消息為空 break print(data) #列印回應消息 tcpCliSock.close() #關閉客戶端socket
python3.6下
SocketServer模組在python3中已經更名為socketserver。
伺服器端程式碼為
#coding:utf-8 import socketserver from time import ctime print("=====================SocketServer TCP伺服器====================="); HOST = '' #主機號為空白表示可以使用任何可用的地址。 PORT = 21567 #埠號 ADDR = (HOST, PORT) class MyRequestHandler(socketserver.StreamRequestHandler): #StreamRequestHandler實現TCP/UDP伺服器的服務處理器 def handle(self): #重寫接收響應函數 print('連接到:', self.client_address) data = self.rfile.readline().strip() print(data) self.wfile.write(bytes('[%s] %s' % (ctime(), data.decode('utf-8')),'utf-8')) tcpSerSock = socketserver.TCPServer(ADDR, MyRequestHandler) print('等待連接...') tcpSerSock.serve_forever()
客戶端程式碼為
#coding:utf-8 from socket import * print("=====================SocketServer TCP客戶端====================="); HOST = '127.0.0.1' #本機測試 PORT = 21567 BUFSIZ = 1024 ADDR = (HOST, PORT) while True: tcpCliSock = socket(AF_INET, SOCK_STREAM) #創建客戶端套接字 tcpCliSock.connect(ADDR) #發起TCP連接 data = input('> ') #接收用戶輸入 if not data: #如果用戶輸入為空,直接回車就會發送"",""就是代表false break tcpCliSock.send(bytes(data+'n','utf-8')) #客戶端發送消息,必須發送位元組數組 buffer = tcpCliSock.recv(BUFSIZ) #接收回應消息,接收到的是位元組數組 if not buffer: #如果接收伺服器資訊失敗,或響應消息為空 break print(str(buffer,'utf-8')) #列印回應消息 tcpCliSock.close() #關閉客戶端socket
伺服器類型
5種類型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。注意:BaseServer不直接對外服務。
伺服器對象
•class SocketServer.BaseServer:這是模組中的所有伺服器對象的超類。它定義了介面,如下所述,但是大多數的方法不實現,在子類中進行細化。
•BaseServer.fileno():返回伺服器監聽套接字的整數文件描述符。通常用來傳遞給select.select(), 以允許一個進程監視多個伺服器。
•BaseServer.handlerequest():處理單個請求。處理順序:getrequest(), verifyrequest(), processrequest()。如果用戶提供handle()方法拋出異常,將調用伺服器的handleerror()方法。如果self.timeout內沒有請求收到, 將調用handletimeout()並返回handle_request()。
•BaseServer.serveforever(pollinterval=0.5): 處理請求,直到一個明確的shutdown()請求。每poll_interval秒輪詢一次shutdown。忽略self.timeout。如果你需要做周期性的任務,建議放置在其他執行緒。
•BaseServer.shutdown():告訴serve_forever()循環停止並等待其停止。python2.6版本。
•BaseServer.addressfamily: 地址家族,比如socket.AFINET和socket.AF_UNIX。
•BaseServer.RequestHandlerClass:用戶提供的請求處理類,這個類為每個請求創建實例。
•BaseServer.server_address:伺服器偵聽的地址。格式根據協議家族地址的各不相同,請參閱socket模組的文檔。
•BaseServer.socketSocket:伺服器上偵聽傳入的請求socket對象的伺服器。
伺服器類支援下面的類變數:
•BaseServer.allowreuseaddress:伺服器是否允許地址的重用。默認為false ,並且可在子類中更改。
•BaseServer.requestqueuesize
請求隊列的大小。如果單個請求需要很長的時間來處理,伺服器忙時請求被放置到隊列中,最多可以放requestqueuesize個。一旦隊列已滿,來自客戶端的請求將得到 「Connection denied」錯誤。默認值通常為5 ,但可以被子類覆蓋。
•BaseServer.sockettype:伺服器使用的套接字類型; socket.SOCKSTREAM和socket.SOCK_DGRAM等。
•BaseServer.timeout:超時時間,以秒為單位,或 None表示沒有超時。如果handlerequest()在timeout內沒有收到請求,將調用handletimeout()。
下面方法可以被子類重載,它們對伺服器對象的外部用戶沒有影響。
•BaseServer.finish_request():實際處理RequestHandlerClass發起的請求並調用其handle()方法。常用。
•BaseServer.get_request():接受socket請求,並返回二元組包含要用於與客戶端通訊的新socket對象,以及客戶端的地址。
•BaseServer.handleerror(request, clientaddress):如果RequestHandlerClass的handle()方法拋出異常時調用。默認操作是列印traceback到標準輸出,並繼續處理其他請求。
•BaseServer.handle_timeout():超時處理。默認對於forking伺服器是收集退出的子進程狀態,threading伺服器則什麼都不做。
•BaseServer.processrequest(request, clientaddress) :調用finish_request()創建RequestHandlerClass的實例。如果需要,此功能可以創建新的進程或執行緒來處理請求,ForkingMixIn和ThreadingMixIn類做到這點。常用。
•BaseServer.server_activate():通過伺服器的構造函數來激活伺服器。默認的行為只是監聽伺服器套接字。可重載。
•BaseServer.server_bind():通過伺服器的構造函數中調用綁定socket到所需的地址。可重載。
•BaseServer.verifyrequest(request, clientaddress):返回一個布爾值,如果該值為True ,則該請求將被處理,反之請求將被拒絕。此功能可以重寫來實現對伺服器的訪問控制。默認的實現始終返回True。client_address可以限定客戶端,比如只處理指定ip區間的請求。常用。
請求處理器
處理器接收數據並決定如何操作。它負責在socket層之上實現協議(i.e., HTTP, XML-RPC, or AMQP),讀取數據,處理並寫反應。可以重載的方法如下:
•setup(): 準備請求處理. 默認什麼都不做,StreamRequestHandler中會創建文件類似的對象以讀寫socket.
•handle(): 處理請求。解析傳入的請求,處理數據,並發送響應。默認什麼都不做。常用變數:self.request,self.client_address,self.server。
•finish(): 環境清理。默認什麼都不做,如果setup產生異常,不會執行finish。
通常只需要重載handle。self.request的類型和數據報或流的服務不同。對於流服務,self.request是socket 對象;對於數據報服務,self.request是字元串和socket 。可以在子類StreamRequestHandler或DatagramRequestHandler中重載,重寫setup()和finish() ,並提供self.rfile和self.wfile屬性。self.rfile和self.wfile可以讀取或寫入,以獲得請求數據或將數據返回到客戶端。