標籤:檔案 繼承 資訊 while lag daemon 衍生類別 overwrite 迴圈
文章出處: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 socketimport threadingimport selectdef 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 socketserverclass 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 socketip_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‘: breaksk.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 sourceclass MyHandler(source.BaseHandler): def execute(self): print("Event-drive execute myhandler") source.event_list.append(MyHandler)source.run()
如上述代碼,事件驅動只不過是架構規定了執行順序,程式員在使用架構時,可以向原執行順序中註冊“事件”,從而在架構執行時可以出發登入的“事件”。
基於事件驅動Socket
from twisted.internet import protocolfrom twisted.internet import reactorclass 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, protocolfrom twisted.web.client import getPagefrom twisted.internet import reactorimport timeclass 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模組