Python實戰之SocketServer模組

來源:互聯網
上載者:User

標籤:檔案   繼承   資訊   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之所以可以同時處理請求得益於 selectThreading 兩個東西,其實本質上就是在伺服器端為每一個用戶端建立一個線程,當前線程用來處理對應用戶端的請求,所以,可以支援同時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模組

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.