網絡IO模型

爲了更好地瞭解IO模型,咱們須要事先回顧下:同步、異步、阻塞、非阻塞1.網絡傳輸中的兩個階段  分別是 waitdata 和 copydata    send---copydata    recv---waitdata + copydata 記住這兩點很重要,由於這些IO模型的區別就是在兩個階段上各有不一樣的狀況。2.阻塞IO    不管是線程 進程  仍是線程 進程池 通通都是阻塞IO    應用程序  發送 系統調用---操做系統等待數據(wait)---數據準備好 return data    因此,blocking IO的特色就是在IO執行的兩個階段(等待數據和拷貝數據兩個階段)都被block了。3.非阻塞IO    協程是一種非阻塞IO    server.setblocking(False)將阻塞修改成非阻塞    最直接體現 recv send accept 都不會阻塞 會當即執行    可是不能保證立馬就有數據 沒有數據拋出異常    咱們須要手動捕獲異常  捕獲異常後能夠處理別的任務    能夠實現單線程併發的效果  但會大量佔用CPU資源    while True:        pass    因此,在非阻塞式IO中,用戶進程實際上是須要不斷的主動詢問kernel數據準備好了沒有。4.多路複用    管理鏈接的一種方式    爲何使用它? 相對於非阻塞IO下降無用的系統調用    怎麼管?        核心函數select  幫你檢測全部的鏈接 找出能夠被處理(能夠讀寫)的鏈接                        (默認時阻塞的  阻塞到有任意一個鏈接能夠被處理)    結論: select的優點在於能夠處理多個鏈接,不適用於單個鏈接    一  建立鏈接 和管理鏈接        1.建立服務器socket對象        2.將服務器對象交給select來管理        3.一旦有客戶端發起鏈接 select將不在阻塞        4.select將返回一個可讀的socket對象(第一次只有服務器)        5.服務器的可讀表明有鏈接請求 須要執行accept  返回一個客戶端鏈接conn  因爲是非阻塞 不能當即去recv        6.把客戶端socket對象也交給select來管理  將conn加入兩個被檢測的列表中        7.下一次檢測到可讀的socket 多是服務器 也可能客戶端 因此加上判斷  服務器就accept 客戶端就recv        8.若是檢測到有可寫(能夠send就是系統緩存可用)的socket對象 則說明能夠向客戶端發送數據了        7 和 8 執行順序不是固定的    二 處理數據收發        兩個須要捕獲異常的地方        1.recv  執行第7步 表示能夠讀 爲何異常 只有一種可能客戶端斷開鏈接            還須要加上if not 判斷是否有數據  ;linux下 對方下線不會拋出異常 會收到空消息        2.send  執行第8步 表示能夠寫 爲何異常 只有一種可能客戶端斷開鏈接)    強調:        1. 若是處理的鏈接數不是很高的話,使用select/epoll的web server不必定比            使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。            select/epoll的優點並非對於單個鏈接能處理得更快,而是在於能處理更多的鏈接。        2. 在多路複用模型中,對於每個socket,通常都設置成爲non-blocking,可是,如上圖所示,            整個用戶的process實際上是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。    結論: select的優點在於能夠處理多個鏈接,不適用於單個鏈接5.異步IO 網絡IO+本地IO 都適用    IO包括  網絡IO 本地IO    上面的三種IO模型描述的都是網絡IO,不是本地IO的問題    解決的方案就是:        將同步的IO操做改爲異步的IO操做  在IO期間 能夠執行其餘的任務    最終的解決方案就是協程 使用asyncio模塊 該模快實現異步IO 內部使用協程實現它的流程:用戶進程發起read操做以後,馬上就能夠開始去作其它的事。而另外一方面,從kernel的角度,    當它受到一個asynchronous read以後,首先它會馬上返回,因此不會對用戶進程產生任何block。    而後,kernel會等待數據準備完成,而後將數據拷貝到用戶內存,    當這一切都完成以後,kernel會給用戶進程發送一個signal,告訴它read操做完成了。socketserver    是什麼? 對服務器端的socket的封裝            封裝了多線程 多進程 IO模型,支撐高併發 高併發 的socket套接字    爲何用? 簡化代碼    使用方法:        socketserver  (forkingUDP forkingTCP windows沒法使用)        核心類 ThreadingUDPServer ThreadingTCPServer        ThreadingTCPServer 實例化時  傳入服務器地址 和  自定義的一個數據處理類        自定義類須要繼承BaseRequestHandler類中需包含handle函數        對象調用serve_foreverTCP服務端import  socketserverclass MyHandler(socketserver.BaseRequestHandler):    def handler(self):        while True:            try:                data=self.request.recv(1024)                if not data:break                print(data.decode('utf-8'))                self.request.send(data.upper())            except ConnectionResetError:                break        self.request.close()if __name__ == '__main__':    server=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyHandler)    server.serve_forever()UDP服務端import  socketserverclass MyHandler(socketserver.BaseRequestHandler):    def handle(self):        data,server=self.request        print(data.decode('utf-8'))        server.sendto(data.upper(),self.client_address)if __name__ == '__main__':    server=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyHandler)    server.serve_forever()
相關文章
相關標籤/搜索