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可以讀取或寫入,以獲得請求數據或將數據返回到客戶端。