select系統調用時用來讓我們的程式監視多個檔案句柄的狀态變化的。程式會停在select這裡等待,直到被監視的檔案句柄有一個或多個發生了狀态改變。
關于檔案句柄,其實就是一個整數,通過socket函數的聲明就明白了:
int socket(int domain, int type, int protocol);
我們最熟悉的句柄是0、1、2三個,0是标準輸入,1是标準輸出,2是标準錯誤輸出。0、1、2是整數表示的,對應的file *結構的表示就是stdin、stdout、stderr。
繼續上面的select,就是用來監視某個或某些句柄的狀态變化的。select函數原型如下:
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
函數的最後一個參數timeout是一個逾時時間值。其類型是struct timeval *,即一個struct timeval結構的變量的指針,是以我們在程式裡要聲明一個struct timeval tv;然後把變量tv的位址&tv傳遞給select函數。struct timeval結構如下:
struct timeval
{
long tv_sec; //seconds
long tv_usec; //microseconds
};
第2、3、4三個參數是一樣的類型;fd_set *,即我們在程式裡要申請幾個fd_set類型的變量,比如rdfds,wtfds,exfds,然後把這個變量的位址&rdfds,&wtfds,&exfds傳遞給select函數。這三個參數都是一個句柄的集合,第一個rdfds是用來儲存這樣的句柄的:當句柄的狀态變成可讀時系統就告訴select函數傳回,同理第二個函數是指向有句柄狀态變成可寫時系統就會告訴select函數傳回,同理第三個參數exfds是特殊情況,即句柄上有特殊情況發生時系統會告訴select函數傳回。特殊情況比如對方通過一個socket句柄發來了緊急資料。如果我們程式裡隻想檢測某個socket是否有資料可讀,我們可以這樣:
fd_set rdfds;
struct timeval tv;
int ret;
fd_zero(&rdfds);
fd_set(socket, &rdfds);
tv.tv_sec = 1;
tv.tv_uses = 500;
ret = select (socket + 1, %rdfds, null, null, &tv);
if(ret < 0) perror (“select”);
else if (ret = = 0) printf(“time out”);
else {
printf(“ret = %d/n”,ret);
if(fd_isset(socket, &rdfds)){
/* 讀取socket句柄裡的資料 */
recv( );
}
注意select函數的第一個參數,是所有加入集合的句柄值的最大那個那個值還要加1.比如我們建立了3個句柄;
int sa, sb, sc;
sa = socket(……);
connect (sa,….);
sb = socket(….);
connect (sb,…);
sc = socket(….);
connect(sc,…);
fd_set(sa, &rdfds);
fd_set(sb, &rdfds);
fd_set(sc, &rdfds);
在使用select函數之前,一定要找到3個句柄中的最大值是哪個,我們一般定義一個變量來儲存最大值,取得最大socket值如下:
int maxfd = 0;
if(sa > maxfd) maxfd = sa;
if(sb > maxfd) maxfd = sb;
if(sc > maxfd) maxfd = sc;
然後調用select函數:
ret = select (maxfd+1, &rdfds, null, null,&tv);
同樣的道理,如果我們是檢測使用者是否按了鍵盤進行輸入,我們就應該把标準輸入0這個句柄放到select裡來檢測,如下:
fd_set(0, &rdfds);
tv.tv_usec = 0;
ret = select (1, &rdfds,null,null,&tv);
if(ret < 0) perror(“select”);
else if (ret = = 0) printf (“time out/n”);
else{
scanf(“%s”,buf);