socketserver
是標準庫中的一個高級模塊,socketserver
模塊是python提供的內置的用於快捷開發服務端程序的一個服務器框架,經過封裝大量實現的方式減小開發人員工做量的同時能快捷開發出具備較高質量的服務端程序; 該模塊中類的繼承關係以下:python
+------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+
socketserver模塊主要包含的非Unix服務器類有:TCPserver
、UCPserver
、ThreadingTCPServer(ThreadingMixIn, TCPServer)
、ThreadingUDPServer(ThreadingMixIn, UDPServer)
、ForkingTCPServer(ForkingMixIn, TCPServer)
、ForkingUDPServer(ForkingMixIn, UDPServer)
,其中的ThreadingMixIn、ForkingMixIn分別用來實現線程級別、進程級別的異步瀏覽器
TCPServer
,基本的網絡同步TCP服務器;服務器
UDPServer
,基本的網絡同步UDP服務器;網絡
UnixStreamServer
,使用UNIX域套接字實現面向數據流協議的服務器,繼承自TCPServer;多線程
UnixDatagramServer
,使用UNIX域套接字實現數據報協議的服務器,繼承自UDPServer;框架
其中經常使用的是TCPServer
和UDPServer
,且這四個類型都是同步地處理請求,也就是說一個請求沒有完成以前是不會處理下一個請求的,可是這種模式不適合生產環境。 因此這個模塊還提供了兩種支持異步處理的類,ForkingMixIn
和ThreadingMixIn
,繼承自這兩個類的服務端在處理新的客戶端鏈接時不會阻塞,而是建立新的進程或線程來專門處理客戶端的請求:異步
ForkingMixIn
,將UNIX進程分支添加到服務器的混合方法,使用該方法可讓服務器服務多個客戶,(爲每個客戶端請求派生一個新的進程去專門處理);socket
ThreadingMixIn
,修改服務器的混合類,可使用多線程服務多個客戶端,(爲每個客戶端請求派生一個新的線程去專門處理);tcp
咱們先編寫服務器端的代碼,首先導入了socketserver模塊,而後自定義一個Handler類,這個類是繼承了socketserver模塊中的BaseRequestHandler。 咱們能夠按住Ctrl鍵而後鼠標點擊這個類,看一下這個類的方法有哪些,除了一個__init__()
方法,是否是還有三個沒有實現的setup()、handle()、finish()方法呀,那這裏就在Handler類中覆蓋BaseRequestHandler類的handle()方法,打印一些信息。 而後選擇一個合適的Server類,初始化且綁定地址和新建的Handler處理類,最後調用server實例的serve_forever()
方法,啓動server。函數
import socketserver import threading from pprint import pprint class Handler(socketserver.BaseRequestHandler): def handle(self): print('當前的server類型: {}'.format(self.server)) print('當前的socket鏈接對象: {}'.format(self.request)) print('當前的客戶端地址: {}'.format(self.client_address)) print('線程列表: {}'.format(threading.enumerate())) print('當前的線程:{}'.format(threading.current_thread())) server = socketserver.TCPServer(('127.0.0.1', 9000), Handler) server.serve_forever()
咱們運行一下這個代碼,發現控制檯並無輸出,那咱們能夠到命令窗口查看一下TCP是否鏈接,命令行爲:netstat -anp tcp | grep 9000
,而後顯示的是正在監聽並無鏈接,咱們能夠直接使用瀏覽器請求9000端口,由於HTTP是在TCP之上實現的一種協議,而後在瀏覽器中訪問127.0.0.1: 9000
,控制檯中打印出不少的信息:
當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63944)> 當前的客戶端地址: ('127.0.0.1', 63944) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)> 當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63945)> 當前的客戶端地址: ('127.0.0.1', 63945) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)> 當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63948)> 當前的客戶端地址: ('127.0.0.1', 63948) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)> 當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63949)> 當前的客戶端地址: ('127.0.0.1', 63949) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)> 當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63950)> 當前的客戶端地址: ('127.0.0.1', 63950) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)> 當前的server類型: <socketserver.TCPServer object at 0x000000000277DD68> 當前的socket鏈接對象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63951)> 當前的客戶端地址: ('127.0.0.1', 63951) 線程列表: [<_MainThread(MainThread, started 7612)>] 當前的線程:<_MainThread(MainThread, started 7612)>
那咱們總結一下,socketserver模塊建立服務器步驟:
setup()
:該方法在handle()以前調用,默認什麼都不作,若是但願服務器實現更多鏈接設置,則無需調用該方法;
handle()
:調用該方法執行實際的請求操做,調用函數能夠不帶任何參數,默認什麼都不作;
finish()
: 環境清理,在handle()以後執行清除操做,默認什麼都不作,若是setup()和handle()方法都不生成異常,則無需調用該方法;
############################## 服務器端 ############################## import socketserver import threading class Handler(socketserver.BaseRequestHandler): clients = {} def setup(self): super().setup() self.event = threading.Event() self.clients[self.client_address] = self.request def handle(self): super().setup() while not self.event.is_set(): data = self.request.recv(1024).decode() if data == 'quit': break msg = "{} 說 {}".format(self.client_address, data).encode() print(self.clients) if self.client_address in self.clients.keys(): self.clients[self.client_address].send(msg) # if request self.clients.values(): # request.send(msg) print('server end') def finish(self): super().finish() self.clients.pop(self.client_address) self.event.set() if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1', 9000), Handler) threading.Thread(target=server.serve_forever, daemon=True).start() while True: cmd = input('請輸入您想說的話: ').strip() if cmd == 'quit': print(cmd) server.shutdown() server.server_close() break else: print('您能夠輸入quit來中止服務器! ')
############################## 客戶端 ############################## import socket # 建立TCP鏈接 socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_instance.connect(('127.0.0.1', 9000)) while True: cmd = input("請輸入您想說的話:") socket_instance.send(cmd.encode()) data = socket_instance.recv(1024).decode() print(data)