linux網絡編程:select()函數以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET(轉)

從別人的博客中轉載過來了這一篇文章,通過從新編輯排版以後展示於此,作一個知識點保存與學習。html

select函數用於在非阻塞中,當一個套接字或一組套接字有信號時通知你,系統提供select函數來實現多路複用輸入/輸出模型,原型:數據結構

int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);socket

 

所在的頭文件爲:#include <sys/time.h> 和#include <unistd.h>函數

 

  先對函數中的參數作一個簡單的介紹。參數maxfd是須要監視的最大的文件描述符值+1;rdset,wrset,exset分別對應於須要檢測的可讀文件描述符的集合,可寫文件描述符的集 合及異常文件描述符的集合。struct timeval結構用於描述一段時間長度,若是在這個時間內,須要監視的描述符沒有事件發生則函數返回,返回值爲0。 
    在這些參數中有一個相似於結構體的東西,fd_set,這是什麼的名字,咱們先來看看這個所具備的含義吧。這是一組文件描述字(fd)的集合,它用一位來表示一個fd,等等,文件描述字,熟悉吧,以前都把這個當作一個文件的路徑保存的地方了,也就是當作是一個文件的標誌哦,現不在作猜測了,看看下文是怎麼介紹的吧。
學習

 

    對於fd_set類型經過下面四個宏來操做:測試

 

  FD_ZERO(fd_set *fdset) 將指定的文件描述符集清空,在對文件描述符集合進行設置前,必須對其進行初始化,若是不清空,因爲在系統分配內存空間後,一般並不做清空處理,因此結果是不可知的。
    FD_SET(fd_set *fdset) 用於在文件描述符集合中增長一個新的文件描述符。
    FD_CLR(fd_set *fdset) 用於在文件描述符集合中刪除一個文件描述符。
    FD_ISSET(int fd,fd_set *fdset) 用於測試指定的文件描述符是否在該集合中。
spa

 

    如今,UNIX系統一般會在頭文件<sys/select.h>中定義常量FD_SETSIZE,它是數據類型fd_set的描述字數量,其值一般是1024,這樣就能表示<1024的fd。
線程

 

    好了在研究了一番關於fd_set的信息以後,再回到對select函數的理解上來吧。htm

 

  功能:測試指定的fd可讀?可寫?有異常條件待處理?
  readset  用來檢查可讀性的一組文件描述字。
  writeset 用來檢查可寫性的一組文件描述字。
  exceptset用來檢查是否有異常條件出現的文件描述字。(注:不包括錯誤)
  timeout  用於描述一段時間長度,若是在這個時間內,須要監視的描述符沒有事件發生則函數返回,返回值爲0。
blog

 

  對於select函數的功能簡單的說就是對文件fd作一個測試。測試結果有三種可能:
    1.timeout=NULL(阻塞:select將一直被阻塞,直到某個文件描述符上發生了事件)
    2.timeout所指向的結構設爲非零時間(等待固定時間:若是在指定的時間段裏有事件發生或者時間耗盡,函數均返回)
    3.timeout所指向的結構,時間設爲0(非阻塞:僅檢測描述符集合的狀態,而後當即返回,並不等待外部事件的發生)

 

 

 

    返回值:返回對應位仍然爲1的fd的總數。注意啦:只有那些可讀,可寫以及有異常條件待處理的fd位仍然爲1。不然爲0哦。舉個例子,好比recv(), 在沒有數據到來調用它的時候,你的線程將被阻塞,若是數據一直不來,你的線程就要阻塞好久.這樣顯然很差。因此採用select來查看套節字是否可讀(也就是是否有數據讀了) 。
步驟以下——
socket s;
.....
fd_set set;
while(1)
{
FD_ZERO(&set);//將你的套節字集合清空
FD_SET(s, &set);//加入你感興趣的套節字到集合,這裏是一個讀數據的套節字s
select(0,&set,NULL,NULL,NULL);//檢查套節字是否可讀,
//不少狀況下就是是否有數據(注意,只是說不少狀況)
//這裏select是否出錯沒有寫
if(FD_ISSET(s, &set) //檢查s是否在這個集合裏面,
{ //select將更新這個集合,把其中不可讀的套節字去掉
//只保留符合條件的套節字在這個集合裏面
recv(s,...);
}
//do something here
}

 

    理解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)的值。

 

   (2)能夠有效突破select可監控的文件描述符上限。
   (3)將fd加入select監控集的同時,還要再使用一個數據結構array保存放到select監控集中的fd,一是用於再select 返回後,array做爲源數據和fd_set進行FD_ISSET判斷。二是select返回後會把之前加入的但並沒有事件發生的fd清空,則每次開始 select前都要從新從array取得fd逐一加入(FD_ZERO最早),掃描array的同時取得fd最大值maxfd,用於select的第一個 參數。
   (4)可見select模型必須在select前循環array(加fd,取maxfd),select返回後循環array(FD_ISSET判斷是否有時間發生)。

 


使用select函數的過程通常是:
    先調用宏FD_ZERO將指定的fd_set清零,而後調用宏FD_SET將須要測試的fd加入fd_set,接着調用函數select測試fd_set中的全部fd,最後用宏FD_ISSET檢查某個fd在函數select調用後,相應位是否仍然爲1。

 

如下是一個測試單個文件描述字可讀性的例子:
int isready(int fd)
{
int rc;
fd_set fds;
struct tim tv;
FD_ZERO(&fds);
FD_SET(fd,&fds);
tv.tv_sec = tv.tv_usec = 0;
rc = select(fd+1, &fds, NULL, NULL, &tv);
if (rc < 0) //error
return -1;
return FD_ISSET(fd,&fds) ? 1 : 0;
}

原文出處:http://blog.sina.com.cn/s/blog_a43aa27401015kt9.html

相關文章
相關標籤/搜索