雖然說用Python編寫簡單的網絡程序很方便,但複雜一點的網絡程序仍是用現成的框架比較好。這樣就能夠專心事務邏輯,而不是套接字的各類細節。SocketServer模塊簡化了編寫網絡服務程序的任務。同時SocketServer模塊也是Python標準庫中不少服務器框架的基礎。python
socketserver模塊能夠簡化網絡服務器的編寫,Python把網絡服務抽象成兩個主要的類,一個是Server類,用於處理鏈接相關的網絡操做,另一個則是RequestHandler類,用於處理數據相關的操做。而且提供兩個MixIn 類,用於擴展 Server,實現多進程或多線程。服務器
server端和客戶端持續聊天的示例,可是同時只能和一個客戶端進行通訊,並無併發的效果。網絡
from socket import * ip_port=('127.0.0.1',8880) tcp_socket_server=socket() tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) while 1: conn,addr=tcp_socket_server.accept() print('客戶端',addr) while 1: client_data=conn.recv(1024) if len(client_data)==0: print("客戶端斷開鏈接,等待新的用戶鏈接....") break print ("接受數據 >>>",str(client_data,"utf8")) response=input("響應數據 >>>") conn.sendall(bytes(response,"utf8")) conn.close()
import socket ip_port = ('127.0.0.1',8888) sock = socket.socket() sock.connect(ip_port) print ("客戶端啓動:") while True: inp = input('發送數據 >>>') if inp == 'exit': break sock.sendall(bytes(inp,"utf8")) server_response=sock.recv(1024) print ("服務端響應數據 >>>",str(server_response,"utf8")) sock.close()
import socketserver class MyServer(socketserver.BaseRequestHandler): # 必須繼承這個類 def handle(self): """ 併發的業務邏輯 conn(客戶端套接字對象):self.request :return: """ while 1: client_data=self.request.recv(1024) if client_data.decode("utf8") == "exit": print("客戶端斷開鏈接,等待新的用戶鏈接....") break print("接受數據 >>>",str(client_data, "utf8")) response=input("響應數據 >>>") self.request.sendall(bytes(response, "utf8")) self.request.close() # 封裝TCP協議相關的套接字對象 server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer) # 第一個參數是ip+port,第二個參數是類名MyServer server.serve_forever()
class MyServer(socketserver.BaseRequestHandler): # 必須繼承這個類 def handle(self): """ 併發的業務邏輯 conn(客戶端套接字對象):self.request :return: """ while 1: client_data=self.request.recv(1024) if len(client_data)==0: print("客戶端斷開鏈接,等待新的用戶鏈接....") break print ("接受數據 >>>",str(client_data,"utf8")) response=input("響應數據 >>>") self.request.sendall(bytes(response,"utf8")) self.request.close()
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer) # 第一個參數是ip+port,第二個參數是類名MyServer
server.serve_forever()
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
在socketserver.py中查看到這句源碼,混合繼承了ThreadingMixIn和TCPServer類。根據廣度優先先查看左邊的類。多線程
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address):... def process_request(self, request, client_address):...
發現這個類並無__init__方法,只有兩個實例方法。所以再回去查看TCPServer類。併發
class TCPServer(BaseServer): address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = False def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): """ 構造函數 :param server_address: 接收的要綁定的元組('127.0.0.1', 8880) :param RequestHandlerClass: 接收功能類MyServer :param bind_and_activate: """ BaseServer.__init__(self, server_address, RequestHandlerClass) # 執行BaseServer的__init__方法 self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise """後面代碼省略"""
注意__init__方法接收的參數,server_address: 接收的要綁定的元組('127.0.0.1', 8880);RequestHandlerClass: 接收功能類MyServer。框架
__init__方法第一步就是執行BaseServer的__init__方法。socket
class BaseServer: timeout = None def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address # 實例變量賦值 self.RequestHandlerClass = RequestHandlerClass # 實例變量賦值 self.__is_shut_down = threading.Event() self.__shutdown_request = False """代碼省略"""
能夠看到主要是作了一個實例變量賦值tcp
在執行完BaseServer的__init__方法後,接下里開始建立socket對象:ide
self.socket = socket.socket(self.address_family, self.socket_type)
socket對象建立完成後,執行socket.bind和socket.listen方法:函數
class TCPServer(BaseServer): def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): if bind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise def server_bind(self): """Called by constructor to bind the socket. May be overridden. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) # bind元組地址 self.server_address = self.socket.getsockname() def server_activate(self): """Called by constructor to activate the server. May be overridden. """ self.socket.listen(self.request_queue_size) # listen,這裏默認值也是5
1.self.socket 2.self.socket.bind() 3.self.socket.listen(5)
這三步完成後,初始化也就完成了。
因爲server是ThreadingTCPServer的實例對象,所以依次追溯ThreadingTCPServer、ThreadingMixIn、TCPServer、BaseServer類,最終在BaseServer類中找到serve_forever方法。
class BaseServer: def serve_forever(self, poll_interval=0.5): self.__is_shut_down.clear() try: with _ServerSelector() as selector: # IO多路複用監聽 selector.register(self, selectors.EVENT_READ) while not self.__shutdown_request: # 也是監聽 ready = selector.select(poll_interval) if ready: self._handle_request_noblock() self.service_actions() finally: self.__shutdown_request = False self.__is_shut_down.set()
前面兩步都是監聽相關的操做,直接查看self._handle_request_noblock()源碼。
一樣是依次追溯各個類,最後在BaseServer類中找到方法:
class BaseServer: def _handle_request_noblock(self): try: request, client_address = self.get_request() except OSError: return if self.verify_request(request, client_address): try: self.process_request(request, client_address) except Exception: self.handle_error(request, client_address) self.shutdown_request(request) except: self.shutdown_request(request) raise else: self.shutdown_request(request)
class TCPServer(BaseServer): def get_request(self): """Get the request and client address from the socket. May be overridden. """ return self.socket.accept()
在這裏找到了socket.accept方法,被動接受TCP客戶的鏈接,(阻塞式)等待鏈接到來。
所以_handle_request_noblock中的request, client_address = self.get_request():request就是新的套接字對象(conn),能夠用來接收和發送數據;client_address就是鏈接客戶端的地址。
繼續查看_handle_request_noblock方法的self.process_request(request, client_address)調用。
這裏按照查找順序,在ThreadingMixIn類找到process_request方法。(注意不要找BaseServer裏的process_request,它已經被覆蓋了)
class ThreadingMixIn: def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
每來一個用戶就開啓一個線程,執行self.process_request_thread(request, client_address)方法。
process_request_thread方法也在ThreadingMixIn類中。
class ThreadingMixIn: def process_request_thread(self, request, client_address): try: self.finish_request(request, client_address) except Exception: self.handle_error(request, client_address) finally: self.shutdown_request(request)
按照查找順序在BaseServer中找到finish_request方法。
class BaseServer: def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self)
注意這裏的RequestHandlerClass在TCPServer類中的__init__方法執行時,用RequestHandlerClass來接收功能類MyServer。
這裏是在調用MyServer類的實例化。
class MyServer(socketserver.BaseRequestHandler): # 必須繼承這個類 def handle(self): """ 併發的業務邏輯 conn(客戶端套接字對象):self.request :return: """ while 1: client_data=self.request.recv(1024) if client_data.decode("utf8") == "exit": print("客戶端斷開鏈接,等待新的用戶鏈接....") break print("接受數據 >>>",str(client_data, "utf8")) response=input("響應數據 >>>") self.request.sendall(bytes(response, "utf8")) self.request.close()
可是這個類並無__init__方法,所以須要查看它的父類__init__方法。
class BaseRequestHandler: def __init__(self, request, client_address, server): self.request = request # 新的套接字對象(conn)實例變量賦值 self.client_address = client_address # 鏈接客戶端的地址實例變量賦值 self.server = server self.setup() try: self.handle() # 調用本身的handle方法 finally: self.finish()
在完成新的套接字對象(conn)實例變量賦值和鏈接客戶端的地址實例變量賦值後,調用本身的handle方法。
這個self.handle()調用的不是BaseRequestHandler類的handle方法而是MyServer的handle方法,執行自定義的handle方法。