I/O多路複用方案

1. 本節思惟導圖

  

2. 基本的網絡編程接口

2.1 基於TCP的通訊模型

  

2.2 基於UDP的通訊模型

3. 非阻塞的服務器程序

  file、pipe、fifo、socket在默認建立過程當中都是阻塞的(block),非阻塞的接口相比於阻塞型接口的顯著差別在於,在被調用以後當即返回,在linux中咱們可使用fcntl()函數來設置描述符的狀態html

  fcntl(fd,F_SETFL, O_NOBLOCK)linux

  fcntl函數重點關注第二個參數中的F_SETFL和F_GETFL設置或獲取描述符的狀態編程

  狀態包括:O_NONBLOCK O_APPEND O_ASYNC緩存

  O_DIRCT(最小化或去掉reading和writing的緩存影響.系統將企圖避免緩存你的讀或寫的數據)服務器

  在非阻塞的狀態下,recv()接口在被調用後當即返回,返回值表明了不一樣的含義:網絡

1)recv()返回值大於0,表示接收數據完畢,返回值便是接收到的字節數
(2)recv()返回值爲0,表示連接已經正常斷開
(3)recv()返回值爲-1,且errno等於EAGAIN,表示recv操做尚未執行完成
(4)recv()返回值爲-1,且error不等於EAGAIN,表示recv操做遇到系統錯誤errno

  注意:毫不推薦循環調用recv()來檢測「操做是否完成」,循環調用recv將大幅度推高CPU佔用率,實際操做系統提供了更爲高效的方法來檢測「操做是否完成」,好比select、epoll異步

4. select()接口的基於事件驅動的服務器模型

  大部分UNIX/LINUX都支持select函數,改函數用於探測多個文件句柄的狀態變化,如下爲select接口的原型:socket

FD_ZERO(int fd, fd_set *fds) // fd_set類型變量的全部位都設爲0
FD_SET(int fd, fd_set *fds ) // 設置變量的某個位置位
FD_ISSET(int fd, fd_set *fds) // 測試某個位置是否被置位
FD_CLR(int fd, fd_set *fds)   // 清除某個位時可使用
int select(int nfds, fd_set *readfds, fd_set *writefds, df_set *exceptfds, struct timeval *timeout)

  關於select更加詳細的說明和使用請參考:http://www.javashuo.com/article/p-tirrufkh-br.html函數

5. 使用epoll實現異步事件通知模型

  已經在前面的博客中總結了epoll用法,具體參考:linux之epollpost

  這裏重點說明下accept的使用技巧:

  (1)阻塞模式accept存在的問題,考慮這種狀況:TCP連接被客戶端夭折,即在服務器調用accept以前,客戶端主動發送RST終止連接,致使剛剛創建的連接從就緒隊列中移除,若是套接口被設置爲阻塞模式,服務器就會一直阻塞在accept調用上,直到其它某個客戶端創建一個新的連接爲止,可是在此期間,服務器單純地阻塞在accept調用上,就需隊列中的其它描述符都得不處處理

  解決方法:把監聽套接口設爲非阻塞,當客戶端在服務器調用accept以前停止某個鏈接時,accept調用能夠當即返回-1,這時源自Berkeley的實現會在內核中處理該事件,並不會將該事件同質epoll。而其它實現把errno設置爲ECONNABORTED或者EPROTo錯誤,咱們能夠忽略這兩個錯誤。

  (2)ET模式相愛的accept存在的問題,考慮這種狀況:多個連接同時到達時,服務器的TCP就緒隊列瞬間基類多喝就緒連接,因爲是邊緣觸發模式,epoll只會通知一次,accept只處理第一個連接,致使TCP就緒隊列中剩下的連接都得不處處理

  解決方法:用while循環包住accept,處理完TCP就緒隊列中的全部連接後再退出循環,如何知道是否處理完就緒隊列中的全部連接呢?accept返回-1而且errno設置爲EAGAIN就表示全部的連接都處理完。

相關文章
相關標籤/搜索