概述
這篇文章是講解如何使用socketserver創建一個異步TCP服務器,其中Python版本爲3.5.1。python
socketserver主要的類
socketserver模塊中的類主要有如下幾個:
一、BaseServer 包含服務器的核心功能與混合類(mix-in)的鉤子功能。這個類主要用於派生,不要直接生成這個類的類對象,能夠考慮使用TCPServer和UDPServer類。
二、TCPServer:基本的網絡同步TCP服務器
三、UDPServer:基本的網絡同步UDP服務器
四、ForkingMixIn:實現了核心的進程化功能,用於與服務器類進行混合(mix-in),以提供一些異步特性。不要直接生成這個類的對象。
五、ThreadingMixIn:實現了核心的線程化功能,用於與服務器類進行混合(mix-in),以提供一些異步特性。不要直接生成這個類的對象。
六、ForkingTCPServer: ForkingMixIn與TCPServer的組合
七、ForkingUDPServer:ForkingMixIn與UDPServer的組合
八、BaseRequestHandler:基本的請求處理類
九、StreamRequestHandler:TCP請求處理類的一個實現
十、DataStreamRequestHandler:UDP請求處理類的一個實現服務器
BaseRequestHandler類
BaseRequestHandler類的實例h能夠實現如下方法:
一、h.handle() 調用該方法執行實際的請求操做。調用該函數能夠不帶任何參數,可是幾個實例變量包含有用的值。h.request包含請求,h.client_address包含客戶端地址,h.server包含調用處理程序的實例。對於TCP之類的數據流服務,h.request屬性是套接字對象。對於數據報服務,它是包含收到數據的字節字符串。
二、h.setup() 該方法在handle()以前調用。默認狀況下,它不執行任何操做。若是但願服務器實現更多鏈接設置(如創建SSL鏈接),能夠在這裏實現。
三、h.finish() 調用本方法能夠在執行完handle()以後執行清除操做。默認狀況下,它不執行任何操做。若是setup()和handle()方法都不生成異常,則無需調用該方法。
網絡
官方例程
首先上官方給出的例程:app
[python] view plain copy <span style="font-size:14px;">import socket import threading import socketserver class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): def handle(self): data = str(self.request.recv(1024), 'ascii') cur_thread = threading.current_thread() response = bytes("{}: {}".format(cur_thread.name, data), 'ascii') self.request.sendall(response) class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): pass def client(ip, port, message): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((ip, port)) sock.sendall(bytes(message, 'ascii')) response = str(sock.recv(1024), 'ascii') print("Received: {}".format(response)) if __name__ == "__main__": # Port 0 means to select an arbitrary unused port HOST, PORT = "localhost", 0 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) ip, port = server.server_address # Start a thread with the server -- that thread will then start one # more thread for each request server_thread = threading.Thread(target=server.serve_forever) # Exit the server thread when the main thread terminates server_thread.daemon = True server_thread.start() print("Server loop running in thread:", server_thread.name) client(ip, port, "Hello World 1") client(ip, port, "Hello World 2") client(ip, port, "Hello World 3") server.shutdown() server.server_close()</span>
client函數是創建一個客戶端,能夠不用管它。主要部分是在於主函數,ThreadedTCPServer類和ThreadedTCPRequestHandler類。ThreadedTCPServer類繼承了BaseRequestHandler類,ThreadedTCPRequestHandler繼承了ThreadingMixIn和TCPServer異步
正常輸入以下:socket
$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3函數
增長功能
上面部分主要是講解官方的例程,下面這一部分是博主本身增長的功能。工具
一、獲取客戶端的ip和port
1 [python] view plain copy 2 3 client_addr = [] 4 5 class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): 6 7 def setup(self): 8 ip = self.client_address[0].strip() # 獲取客戶端的ip 9 port = self.client_address[1] # 獲取客戶端的port 10 print(ip+":"+str(port)+" is connect!") 11 client_addr.append(self.client_address) # 保存到隊列中 12 13 def handle(self): 14 data = str(self.request.recv(1024), 'ascii') 15 cur_thread = threading.current_thread() 16 response = bytes("{}: {}".format(cur_thread.name, data), 'ascii') 17 self.request.sendall(response)
1 [python] view plain copy 2 3 print("\nclient_addr:"+str(client_addr))
1 [python] view plain copy 2 3 class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): 4 5 def setup(self): 6 ip = self.client_address[0].strip() # 獲取客戶端的ip 7 port = self.client_address[1] # 獲取客戶端的port 8 print(ip+":"+str(port)+" is connect!") 9 client_addr.append(self.client_address) # 保存到隊列中 10 11 def handle(self): 12 while True: # while循環 13 data = str(self.request.recv(1024), 'ascii') 14 if data: # 判斷是否接收到數據 15 cur_thread = threading.current_thread() 16 response = bytes("{}: {}".format(cur_thread.name, data), 'ascii') 17 self.request.sendall(response) 18 19 def finish(self): 20 print("client is disconnect!")
1 [python] view plain copy 2 3 client_addr = [] 4 client_socket = [] 5 6 class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): 7 8 def setup(self): 9 ip = self.client_address[0].strip() # 獲取客戶端的ip 10 port = self.client_address[1] # 獲取客戶端的port 11 print(ip+":"+str(port)+" is connect!") 12 client_addr.append(self.client_address) # 保存到隊列中 13 client_socket.append(self.request) # 保存套接字socket 14 15 def handle(self): 16 while True: # while循環 17 data = str(self.request.recv(1024), 'ascii') 18 if data: # 判斷是否接收到數據 19 cur_thread = threading.current_thread() 20 response = bytes("{}: {}".format(cur_thread.name, data), 'ascii') 21 self.request.sendall(response) 22 23 def finish(self): 24 print("client is disconnect!") 25 client_addr.remove(self.client_address) 26 client_socket.remove(self.request)
以後在主函數中經過client_socket隊列調用sendall或sendto方法便可。例如我在主函數這樣寫(已經註釋掉client函數調用):
1 [python] view plain copy 2 3 message = bytes("clientTest\n", "ascii") 4 while True: 5 time.sleep(2) 6 if client_addr: 7 client_socket[0].sendall(message)
修改服務器ip地址爲空及端口爲8080,使用socket調試工具鏈接該服務器,便可每隔2s接收到「clientTest」字符串。
四、服務器接收客戶端數據超時後斷開
1 [python] view plain copy 2 3 class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): 4 5 ip = "" 6 port = 0 7 timeOut = 6 # 設置超時時間變量 8 9 def setup(self): 10 self.ip = self.client_address[0].strip() # 獲取客戶端的ip 11 self.port = self.client_address[1] # 獲取客戶端的port 12 self.request.settimeout(self.timeOut) # 對socket設置超時時間 13 print(self.ip+":"+str(self.port)+"鏈接到服務器!") 14 client_addr.append(self.client_address) # 保存到隊列中 15 client_socket.append(self.request) # 保存套接字socket 16 17 def handle(self): 18 while True: # while循環 19 try: 20 data = str(self.request.recv(1024), 'ascii') 21 except socket.timeout: # 若是接收超時會拋出socket.timeout異常 22 print(self.ip+":"+str(self.port)+"接收超時!即將斷開鏈接!") 23 break # 記得跳出while循環 24 25 if data: # 判斷是否接收到數據 26 cur_thread = threading.current_thread() 27 response = bytes("{}: {}".format(cur_thread.name, data), 'ascii') 28 self.request.sendall(response) 29 30 def finish(self): 31 print(self.ip+":"+str(self.port)+"斷開鏈接!") 32 client_addr.remove(self.client_address) 33 client_socket.remove(self.request)
使用socket調試工具鏈接該服務器後,不發送任何數據,過了6秒鐘後,服務器端主要打印以下數據: