socket編程:多路複用I/O服務端客戶端之select

其實在之前的TCP之中,我們編程實現了多進程,多線程機制下的TCP服務器,但是對於這種的TCP服務器而言,存在太大的資源侷限性。所以我們可以是用I/0模型中的多路複用I/O模型來進行編程。

 

他的具體思想就是:當前進程可以處理多個相應時間,記錄多個描述符,然後控制輪詢時間態,當有響應產生的時候我們就去保存當前響應文件描述符,對他進行連接處理/數據傳輸就OK了。在一個進程進行多個響應時間的答覆情況下,可以大大的節約我們系統所消耗的資源。

 

對這個進行操作的函數就是select():

他的函數原型如下:

 

首先我們不看select函數本身,我們先去看一下select下面的幾個FD函數:

FD = fd,其實很簡單,就是我們的文件描述符操作函數。

關於fd_set*類型的變量:其實就是一個對某種類型時間行爲的文件描述符集合,稱爲描述詞組。

FD_CLR(inr fd,fd_set* set);用來清除描述詞組set中相關fd 的位

FD_ISSET(int fd,fd_set *set);用來測試描述詞組set中相關fd 的位是否爲真

FD_SET(int fd,fd_set*set);用來設置描述詞組set中相關fd的位

FD_ZERO(fd_set *set);用來清除描述詞組set的全部位。

 

然後我們來看一下select函數:

參數nfds是需要監視的最大的文件描述符值+1;

rdset,wrset,exset分別對應於需要檢測的可讀文件描述符的集合,可寫文件描述符的集合及異

常文件描述符的集合。

struct timeval結構用於描述一段時間長度,如果在這個時間內,需要監視的描述符沒有事件

發生則函數返回,返回值爲0。

參數timeout爲結構timeval,用來設置select()的等待時間,其結構定義如下:

 

如果參數timeout設爲:

NULL:則表示select()沒有timeout,select將一直被阻塞,直到某個文件描述符上發生了

事件。

0:僅檢測描述符集合的狀態,然後立即返回,並不等待外部事件的發生。

特定的時間值:如果在指定的時間段裏沒有事件發生,select將超時返回。

 

函數返回值:

執行成功則返回文件描述詞狀態已改變的個數

如果返回0代表在描述詞狀態改變前已超過timeout時間,沒有返回;

當有錯誤發生時則返回-1,錯誤原因存於errno,此時參數readfds,writefds,exceptfds和

timeout的值變成不可預測。錯誤值可能爲:

EBADF 文件描述詞爲無效的或該文件已關閉。

EINTR 此調用被信號所中斷。

EINVAL 參數n 爲負值。

ENOMEM 核心內存不足。

 

理解select模型的關鍵在於理解fd_set,爲說明方便,取fd_set長度爲1字節,fd_set中的每一bit

可以對應一個文件描述符fd。則1字節長的fd_set最大可以對應8個fd。

(1)執行fd_set set; FD_ZERO(&set);則set用位表示是0000,0000。

(2)若fd=5,執行FD_SET(fd,&set);後set變爲0001,0000(第5位置爲1)

(3)若再加入fd=2,fd=1,則set變爲0001,0011

(4)執行select(6,&set,0,0,0)阻塞等待

(5)若fd=1,fd=2上都發生可讀事件,則select返回,此時set變爲0000,0011。注意:沒有事件

發生的fd=5被清空。

 基於上面的討論,可以輕鬆得出select模型的特點:

  (1)可監控的文件描述符個數取決與sizeof(fd_set)的值。我這邊服務 器上sizeof(fd_set)=

512,每bit表示一個文件描述符,則我服務器上支持的最大文件描述符是512*8=4096。據說

可調,另有說雖然可調,但調整上限受於編譯內核時的變量值。本人對調整fd_set的大小不

太感興趣,參考http://www.cppblog.com /CppExplore/archive/2008/03/21/45061.html中的模

型2(1)可以有效突破select可監控的文件描述符上 限。

  (2)將fd加入select監控集的同時,還要再使用一個數據結構array保存放到select監控集

中的fd,一是用於再select 返回後,array作爲源數據和fd_set進行FD_ISSET判斷。二是select

返回後會把以前加入的但並無事件發生的fd清空,則每次開始 select前都要重新從array取得fd

逐一加入(FD_ZERO最先),掃描array的同時取得fd最大值maxfd,用於select的第一個參數。

  (3)可見select模型必須在select前循環array(加fd,取maxfd),select返回後循環array

(FD_ISSET判斷是否有時間發生)。

 

對於利用select函數所編寫的服務器端而言:

TCP服務端所產生的所有sock套接字描述符(文件描述符),其實都可以歸納到可讀文件描述符集合中。

當我們檢測到listen()的sock套接字就建立連接accept(),如果確認到是普通,也就是我們accept()所產生的文件描述符時,就確定這個是一條對於客戶端/服務端的連接鏈路。然後我們就利用相關的函數操作進行信息傳輸,

timeout代表當次所進行的詢問等待時間,需要注意的是就是每一次循環都必須重新設置timeout。

每一次循環開始都要對描述詞組進行初始化,要不然當timeout耗盡而沒有產生響應的話,會改變描述詞組的值,從而產生錯誤。

 

select缺點:

(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大

(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大

(3)select支持的文件描述符數量太小了,默認是1024。
---------------------
作者:q1239678315
來源:CSDN
原文:https://blog.csdn.net/q1239678315/article/details/52672616
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章