Python實戰之SocketServer模塊
文章出處:http://www.cnblogs.com/wupeiqi/articles/5040823.html
SocketServer內部使用 IO多路復用 以及 “多線程” 和 “多進程” ,從而實現並發處理多個客戶端請求的Socket服務端。即:每個客戶端請求連接到服務器時,Socket服務端都會在服務器是創建一個“線程”或者“進程” 專門負責處理當前客戶端的所有請求。
ThreadingTCPServer
ThreadingTCPServer實現的Soket服務器內部會為每個client創建一個 “線程”,該線程用來和客戶端進行交互。
1、ThreadingTCPServer基礎
使用ThreadingTCPServer:
- 創建一個繼承自 SocketServer.BaseRequestHandler 的類
- 類中必須定義一個名稱為 handle 的方法
- 啟動ThreadingTCPServer
內部調用流程為:
- 啟動服務端程序
- 執行 TCPServer.__init__ 方法,創建服務端Socket對象並綁定 IP 和 端口
- 執行 BaseServer.__init__ 方法,將自定義的繼承自SocketServer.BaseRequestHandler 的類 MyRequestHandle賦值給 self.RequestHandlerClass
- 執行 BaseServer.server_forever 方法,While 循環一直監聽是否有客戶端請求到達 ...
- 當客戶端連接到達服務器
- 執行 ThreadingMixIn.process_request 方法,創建一個 “線程” 用來處理請求
- 執行 ThreadingMixIn.process_request_thread 方法
- 執行 BaseServer.finish_request 方法,執行 self.RequestHandlerClass() 即:執行 自定義 MyRequestHandler 的構造方法(自動調用基類BaseRequestHandler的構造方法,在該構造方法中又會調用 MyRequestHandler的handle方法)
import socket import threadingimport select def process(request, client_data): print(request, client_data) conn = request conn.sendall(bytes("Welcom to 10086",encoding=‘utf-8‘)) flag = True while flag: data = conn.recv(1024) if data == ‘exit‘: flag = False elif data == ‘0‘: conn.sendall(bytes("0000",encoding=‘utf-8‘)) else: conn.sendall(bytes("Do it again",encoding="utf-8")) sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.bind((‘127.0.0.1‘,9999)) sk.listen(5) while True: r, w, e = select.select([sk,],[], [], 1) print(‘looping‘) if sk in r: print("Get request") request, client_address = sk.accept() t = threading.Thread(target=process, args=(request, client_address)) t.daemon = False t.start() sk.close()
如精簡代碼可以看出,SocketServer的ThreadingTCPServer之所以可以同時處理請求得益於 select 和 Threading 兩個東西,其實本質上就是在服務器端為每一個客戶端創建一個線程,當前線程用來處理對應客戶端的請求,所以,可以支持同時n個客戶端鏈接(長連接)。
ForkingTCPServer
ForkingTCPServer和ThreadingTCPServer的使用和執行流程基本一致,只不過在內部分別為請求者建立 “線程” 和 “進程”。
基本使用:
服務器端:
import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): # print(self.request, self.client_address, self.server) conn = self.request conn.sendall(bytes(‘Welcome to connect 10086‘,encoding=‘utf-8‘)) Flag = True while Flag: data = conn.recv(1024) if data == ‘exit‘: Flag = False elif data == ‘0‘: conn.sendall(bytes(‘sssssss‘,encoding=‘utf-8‘)) else: conn.sendall(bytes(‘do it again‘,encoding=‘utf8‘)) if __name__ == ‘__main__‘: server = socketserver.ForkingTCPServer((‘127.0.0.1‘,8009),MyServer) server.serve_forever()
客戶端:
import socket ip_port = (‘127.0.0.1‘,8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: data = sk.recv(1024) print("Receive:",data) inp = input("Please input:") sk.sendall(inp) if inp == ‘exit‘: break sk.close()
事件驅動
簡而言之,事件驅動分為二個部分:第一,註冊事件;第二,觸發事件。
自定義事件驅動框架,命名為:“弒君者”:
event_list =[] def run(): for event in event_list: obj = event() obj.execute() class BaseHandler(object): def execute(self): raise Exception("You must overwrite execute")
使用方法:
from day9 import source class MyHandler(source.BaseHandler): def execute(self): print("Event-drive execute myhandler") source.event_list.append(MyHandler) source.run()
如上述代碼,事件驅動只不過是框架規定了執行順序,程序員在使用框架時,可以向原執行順序中註冊“事件”,從而在框架執行時可以出發已註冊的“事件”。
基於事件驅動Socket
from twisted.internet import protocol from twisted.internet import reactor class Echo(protocol.Protocol): def dataReceived(self, data): self.transport.write(data) def main(): factory = protocol.ServerFactory() factory.protocol = Echo reactor.listenTCP(8000, factory) reactor.run() if __name__ == ‘__main__‘: main()
程序執行流程:
- 運行服務端程序
- 創建Protocol的派生類Echo
- 創建ServerFactory對象,並將Echo類封裝到其protocol字段中
- 執行reactor的 listenTCP 方法,內部使用 tcp.Port 創建socket server對象,並將該對象添加到了 reactor的set類型的字段 _read 中
- 執行reactor的 run 方法,內部執行 while 循環,並通過 select 來監視 _read 中文件描述符是否有變化,循環中...
- 客戶端請求到達
- 執行reactor的 _doReadOrWrite 方法,其內部通過反射調用 tcp.Port 類的 doRead 方法,內部 accept 客戶端連接並創建Server對象實例(用於封裝客戶端socket信息)和 創建 Echo 對象實例(用於處理請求) ,然後調用 Echo 對象實例的 makeConnection 方法,創建連接。
- 執行 tcp.Server 類的 doRead 方法,讀取數據,
- 執行 tcp.Server 類的 _dataReceived 方法,如果讀取數據內容為空(關閉鏈接),否則,出發 Echo 的 dataReceived 方法
- 執行 Echo 的 dataReceived 方法
從源碼可以看出,上述實例本質上使用了事件驅動的方法 和 IO多路復用的機制來進行Socket的處理。
from twisted.internet import reactor, protocol from twisted.web.client import getPage from twisted.internet import reactor import time class Echo(protocol.Protocol): def dataReceived(self, data): deferred1 = getPage(‘http://cnblogs.com‘) deferred1.addCallback(self.printContents) deferred2 = getPage(‘http://baidu.com‘) deferred2.addCallback(self.printContents) for i in range(2): time.sleep(1) print ‘execute ‘,i def execute(self,data): self.transport.write(data) def printContents(self,content): print len(content),content[0:100],time.time() def main(): factory = protocol.ServerFactory() factory.protocol = Echo reactor.listenTCP(8000,factory) reactor.run() if __name__ == ‘__main__‘: main()
Python實戰之SocketServer模塊