網絡編程之IO模型——多路複用IO

網絡編程之IO模型——多路複用IO

多路複用IO(IO multiplexing)

IO multiplexing這個詞可能有點陌生,可是若是我說select/epoll,大概就都能明白了。有些地方也稱這種IO方式爲事件驅動IO(event driven IO)。咱們都知道,select/epoll的好處就在於單個process就能夠同時處理多個網絡鏈接的IO。它的基本原理就是select/epoll這個function會不斷的輪詢所負責的全部socket,當某個socket有數據到達了,就通知用戶進程。它的流程如圖:python

當用戶進程調用了select,那麼整個進程會被block,而同時,kernel會「監視」全部select負責的socket,
當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操做,將數據從kernel拷貝到用戶進程。  
這個圖和blocking IO的圖其實並無太大的不一樣,事實上還更差一些。由於這裏須要使用兩個系統調用\(select和recvfrom\),
而blocking IO只調用了一個系統調用\(recvfrom\)。可是,用select的優點在於它能夠同時處理多個connection。

強調:linux

一、若是處理的鏈接數不是很高的話,使用select/epoll的web server不必定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。select/epoll的優點並非對於單個鏈接能處理得更快,而是在於能處理更多的鏈接。web

二、在多路複用模型中,對於每個socket,通常都設置成爲non-blocking,可是,如上圖所示,整個用戶的process實際上是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。編程

結論: select的優點在於能夠處理多個鏈接,不適用於單個鏈接服務器

select網絡IO模型示例網絡

#服務端
from socket import *
import select
server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1',8093))
server.listen(5)
server.setblocking(False)
print('starting...')
rlist=[server,]
wlist=[]
wdata={}
while True:
    rl,wl,xl=select.select(rlist,wlist,[],0.5)
    print(wl)
    for sock in rl:
        if sock == server:
            conn,addr=sock.accept()
            rlist.append(conn)
        else:
            try:
                data=sock.recv(1024)
                if not data:
                    sock.close()
                    rlist.remove(sock)
                    continue
                wlist.append(sock)
                wdata[sock]=data.upper()
            except Exception:
                sock.close()
                rlist.remove(sock)
    for sock in wl:
        sock.send(wdata[sock])
        wlist.remove(sock)
        wdata.pop(sock)
#客戶端
from socket import *
c=socket(AF_INET,SOCK_STREAM)
c.connect(('127.0.0.1',8081))
while True:
    msg=input('>>: ')
    if not msg:continue
    c.send(msg.encode('utf-8'))
    data=c.recv(1024)
    print(data.decode('utf-8'))

select監聽fd變化的過程分析:app

一、用戶進程建立socket對象,拷貝監聽的fd到內核空間,每個fd會對應一張系統文件表,內核空間的fd響應到數據後,
就會發送信號給用戶進程數據已到;
二、用戶進程再發送系統調用,好比(accept)將內核空間的數據copy到用戶空間,同時做爲接受數據端內核空間的數據清除,
這樣從新監聽時fd再有新的數據又能夠響應到了(發送端由於基於TCP協議因此須要收到應答後纔會清除)。

該模型的優勢:socket

一、相比其餘模型,使用select() 的事件驅動模型只用單線程(進程)執行,佔用資源少,不消耗太多 CPU,同時可以爲多客戶端提供服務。
二、若是試圖創建一個簡單的事件驅動的服務器程序,這個模型有必定的參考價值。

該模型的缺點:函數

一、首先select()接口並非實現「事件驅動」的最好選擇。由於當須要探測的句柄值較大時,select()接口自己須要消耗大量時間去輪詢各個句柄。
二、不少操做系統提供了更爲高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。
若是須要實現更高效的服務器程序,相似epoll這樣的接口更被推薦。遺憾的是不一樣的操做系統特供的epoll接口有很大差別,
因此使用相似於epoll的接口實現具備較好跨平臺能力的服務器會比較困難。
三、該模型將事件探測和事件響應夾雜在一塊兒,一旦事件響應的執行體龐大,則對整個模型是災難性的。
相關文章
相關標籤/搜索