select函數

能監視所需文件描述變化的數組

select()的機制中提供一fd_set的數據結構,實際上是一long類型的數組,每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他 文件或命名管道或設備句柄)建立聯繫,建立聯繫的工作由程序員完成,當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執 行了select()的進程哪一Socket或文件可讀或可寫。主要用於Socket通信當中!

編程特點


一、如果一個發現I/O有輸入,讀取的過程中,另外一個也有了輸入,這時候不會產生任何反應。這就需要你的程序語句去用到select函數的時候才知道有數據輸入。
二、程序去select的時候,如果沒有數據輸入,程序會一直等待,直到有數據為止,也就是程序中無需循環和sleep。
Select在Socket編程中還是比較重要的,可是對於初學Socket的人來說都不太愛用Select寫程序,他們只是習慣寫諸如connect、accept、recv或recvfrom這樣的阻塞程序(所謂阻塞方式block,顧名思義,就是進程或是線程執行到這些函數時必須等待某個事件的發生,如果事件沒有發生,進程或線程就被阻塞,函數不能立即返回)。
可是使用Select就可以完成非阻塞(所謂非阻塞方式non-block,就是進程或線程執行此函數時不必非要等待事件的發生,一旦執行肯定返回,以返回值的不同來反映函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生,則返回一個代碼來告知事件未發生,而進程或線程繼續執行,所以效率較高)方式工作的程序,它能夠監視我們需要監視的文件描述符的變化情況——讀寫或是異常。
返回值:準備就緒的描述符數,若超時則返回0,若出錯則返回-1。

操作程序


下面具體解釋:
#include
#include
#include
int select(nfds, readfds, writefds, exceptfds, timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
nfds:select監視的文件句柄數,視進程中打開的文件數而定,一般設為你要監視各文件
中的最大文件號加一。(註:nfds並非一定表示監視的文件句柄數。官方文檔僅指出nfds is the highest-numbered file descriptor in any of the three sets, plus 1. (可在linux環境中通過man select命令查得))
readfds:select監視的可讀文件句柄集合。
writefds: select監視的可寫文件句柄集合。
exceptfds:select監視的異常文件句柄集合。
timeout:本次select()的超時結束時間。(見/usr/sys/select.h,可精確至百萬分之一秒!)
當readfds或writefds中映象的文件可讀或可寫或超時,本次select()
就結束返回。程序員利用一組系統提供的宏在select()結束時便可判
斷哪一文件可讀或可寫,對Socket編程特別有用的就是readfds。

宏解釋


幾行相關的宏解釋如下:
FD_ZERO(fd_set *fdset):清空fdset與所有文件句柄的聯繫。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd與fdset的聯繫。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd與fdset的聯繫。
FD_ISSET(int fd, fd_set *fdset):檢查fdset聯繫的文件句柄fd是否
可讀寫,當>0表示可讀寫。
(關於fd_set及相關宏的定義見/usr/include/sys/types.h)

socket讀寫


這樣,你的socket只需在有東西讀的時候才讀入,大致如下:
...
int sockfd;
fd_set fdR;
struct timeval timeout = ..;
...
for(;;) {
FD_ZERO(&fdR);
FD_SET(sockfd, &fdR);
switch (select(sockfd + 1, &fdR, NULL, NULL , &timeout)) {
case -1:
error handled by u;
break;
case 0:
timeout hanled by u;
break;
default:
if (FD_ISSET(sockfd, &fdR)) {
now u read or recv something;
}
}
}
所以一個FD_ISSET(sockfd)就相當通知了sockfd可讀。
至於struct timeval在此的功能,請man select。不同的timeval設置
使select()表現出超時結束、無超時阻塞和輪詢三種特性。由於
timeval可精確至百萬分之一秒,所以Windows的SetTimer()根本不算
什麼。你可以用select()做一個超級時鐘。