1、單進程服務器
同一時刻只能為一個客戶進行服務, 不能同時為多個客戶服務,類似于找一個“明星”簽字一樣, 客戶需要耐心等待才可以獲取到服務,當服務器為一個客戶端服務時, 若另外的客戶端發起了connect請求, 只要服務器listen的隊列有空閑的位置, 就會為這個新客戶端進行連接, 并且客戶端可以發送數據, 但當服務器為這個新客戶端服務時, 可能一次性把所有數據接收完畢。當recv接收數據時, 返回值為空, 即沒有返回數據, 那么意味著客戶端已經調用了close關閉了; 因此服務器通過判斷recv接收數據是否為空 來判斷客戶端是否已經下線。
雖然多個客戶端幾乎同時像單進程服務器發起了鏈接請求,但單進程服務器卻是一次只為一個客戶端創建accept,即使設置的最大監聽數量是5,在同一時刻,severSock服務器雖然監聽到了五個請求,但有四個是在掛起等待處理的。
2、單進程服務器--非阻塞模式
在上面的單進程服務器中,accept的方式是阻塞的,客戶端與服務器在進行鏈接時,有一個三次握手的過程,如果客戶端不進行第三次握手,那么服務器就會一直阻塞在這里,等著其進行第三次握手,在單進程的條件下,這就導致了其他掛起的客戶端請求遲遲得不到響應,這樣以來就十分浪費CPU的資源。這種情況下,將服務器端套接字設置成非阻塞狀態就會好很多,不僅可以極大的減少CPU的浪費,而且可以模擬多進行對客戶端進行處理。
3、單進程服務器select版
多路復用的模型中, 比較常用的有select模型和epoll模型。 這兩個都是系統接口, 由操作系統提供。 當然, Python的select模塊進行了更高級的封裝。網絡通信被Unix系統抽象為文件的讀寫, 通常是一個設備, 由設備驅動程序提供, 驅動可以知道自身的數據是否可用。設備的文件的資源如果可用( 可讀或者可寫) 則會通知進程, 反之則會讓進程睡眠, 等到數據到來可用的時候, 再喚醒進程。這些設備的文件描述符被放在一個數組中, 然后select調用的時候遍歷這個數組, 如果對于的文件描述符可讀則會返回該文件描述符。 當遍歷結束之后,如果仍然沒有一個可讀設備文件描述符, select讓用戶進程則會睡眠, 直到等待資源可用的時候在喚醒, 遍歷之前那個監視的數組。 每次遍歷都是依次進行判斷的。
總結:
優點:select目前幾乎在所有的平臺上都支持, 其良好跨平臺性也是它的一個優點。
缺點:select的一個缺點在于單個進程能夠監視的文件描述符的數量存在最大限制,在Linux上一般為1024。一般來說這個數量和系統內存關系很大, 具體數目可以cat /proc/sys/fs/filemax察看。 32位機默認是1024個, 64位機默認是2048。對socket進行掃描時是依次掃描的, 即采用輪詢的方法, 效率較低。當套接字比較多的時候, 每次select()都要通過遍歷FD_SETSIZE個Socket來完成調度, 不管哪個Socket是活躍的, 都遍歷一遍,這會浪費很多CPU時間。
4、單進程服務器epoll版
為了改善select版的缺陷,引入了更優化的epoll服務器,epoll版服務器沒有最大并發連接的限制, 能打開的FD(指的是文件描述符, 通俗的理解就是套接字對應的數字編號)的上限遠大于1024。并且效率也得到了提升,不采用輪詢的方式, 不會隨著FD數目的增加效率下降。 只有活躍可用的FD才會調用callback函數,即epoll最大的優點就在于它只管“活躍”的連接, 跟連接總數無關, 因此在實際的網絡環境中, epoll的效率就會遠遠高于select和poll。epoll版在Windows下不支持,需要在Linux/Unix運行。
說明:
EPOLLIN ( 可讀); ? ? ? ? EPOLLOUT ( 可寫); ? ? ?EPOLLET ( ET模式)
epoll對文件描述符的操作有兩種模式: LT(水平觸發) 和ET( 邊沿觸發),LT模式是默認模式, LT模式與ET模式的區別如下:
LT模式: 當epoll檢測到描述符事件發生并將此事件通知應用程序, 應用程序可不立即處理該事件
ET模式: 當epoll檢測到描述符事件發生并將此事件通知應用程序, 應用程序必須立即處理該事件