select函數

函數原型

select函數的原型爲:

#include <sys/select.h>
#include <sys/time.h>

// 返回值:若有就緒描述符,則返回就緒描述符數目;若超時則返回0,出錯返回-1
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

select函數允許進程指示內核等待多個事件中的任何一個的發生,並只在有一個或者多個事件發生,或者經歷一段指定的時間後,才喚醒函數。

結構體

struct fd_set結構體可以理解爲一個集合,這個集合中存放的是文件描述符(file descriptor),即文件句柄。這些文件句柄可以是普通意義的文件,在Unix下任何設備、管道、FIFO等都是文件形式。當然一個socket也是一個文件,因此socket句柄就是一個文件描述符。

fd_set集合可以通過預定義的宏來實現對集合的操作,如清空集合,將給定的文件描述符加入到集合中,將給定的文件描述符從集合中刪除,檢查集合中指定的文件描述符是否可讀或者可寫等。

函數參數

參數maxfdp1是一個整數值,指集合中所有的文件描述符的範圍,即所有的文件描述符的最大值加1。在UNIX中,該值不能計算錯誤。

參數readset是指向fd_set結構的指針。該集合中包含文件描述符,函數監視這些文件描述符的讀變化,即關心是否可以從這些文件描述符中讀取數據。如果集合中有一個文件描述符可讀,則函數返回一個大於0的值,表示有文件描述符可讀。如果沒有可讀的文件描述符,則根據timeout參數再判斷是否超時,若超出timeout的時間,則函數返回0,若發生錯誤,則返回小於0的值。如果不關心集合中任何文件描述符的讀變化,則可將該參數設置爲NULL。

參數writeset是指向fd_set結構的指針。該集合中包含文件描述符,函數監視這些文件描述符的寫變化,即關心是否可以向這些文件描述符中寫入數據。如果集合中有一個文件描述符可寫,則函數返回一個大於0的值,表示有文件描述符可寫。如果沒有可寫的文件描述符,則根據timeout參數再判斷是否超時,若超出timeout的時間,則函數返回0,若發生錯誤,則返回小於0的值。如果不關心集合中任何文件描述符的寫變化,則可將該參數設置爲NULL。

參數exceptset同以上兩個參數的意圖,用來監視文件描述符的錯誤異常。

參數timeout是函數的超時時間,傳入不同的參數,將使得函數具有不同的行爲:

  1. 若以NULL傳入,則將函數置於阻塞狀態,直到監視的文件描述符集合中某個文件描述符發生變化,函數才喚醒;
  2. 若以0秒0毫秒傳入,則將函數置爲純粹的非阻塞狀態,不管文件描述符是否有變化,函數都立即返回執行。
  3. 若以大於0的值傳入,則該值爲超時時間值,函數在該時間值內阻塞,在超時時間內若集合中的文件描述符有變化,函數就返回,否則函數超時後返回。

系統提供的對描述符集操作的宏:

#include <sys/select.h>
#include <sys/time.h>
void FD_SET(int fd, fd_set *fdset);   // 設置文件描述符集fdset中對應於文件描述符fd的位(設置爲1)
void FD_CLR(int fd, fd_set *fdset);   // 清除文件描述符集fdset中對應於文件描述符fd的位(設置爲0)
void FD_ISSET(int fd, fd_set *fdset); // 檢測文件描述符集fdset中對應於文件描述符fd的位是否被設置
void FD_ZERO(fd_set *fdset);          // 清除文件描述符集fdset中的所有位(既把所有位都設置爲0)

範例

// 使用select的cli_io函數,使得在服務器進程終止後客戶可以馬上獲取通知
void cli_io_select(int sockfd, char *mark, FILE *fp)
{
    int maxfdp1, n;
    fd_set rset;
    char sendline[MAXLINE], recvline[MAXLINE];

    FD_ZERO(&rset);

    for ( ; ; )
    {
        FD_SET(fileno(fp), &rset);
        FD_SET(sockfd, &rset);

        // fileno() 函數,將文件流指針轉換爲文件描述符·
        maxfdp1 = max(fileno(fp), sockfd) + 1;

        if (select(maxfdp1, &rset, NULL, NULL, NULL) < 0)
        {
            printf("Error select!\n");
            exit(1);
        }

        if (FD_ISSET(sockfd, &rset))
        {
            if ( (n = read(sockfd, recvline, MAXLINE)) > 0 )
            {
                recvline[n] = '\0';
                fputs(recvline, stdout);
            }
        }

        if (FD_ISSET(fileno(fp), &rset))
        {
            if (fgets(sendline, MAXLINE, fp) == NULL)
            {
                return;
            }

            if (write(sockfd, sendline, strlen(sendline)) < 0)
            {
                printf("Error write!\n");
                exit(1);
            }
        }
    }
}
發佈了68 篇原創文章 · 獲贊 4 · 訪問量 9150
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章