unix網絡編程 select函數和shutdown函數

select函數

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

   #include<sys/select.h> 
   #include<sys/time.h>
   int select(int maxfdp1, fd_set *readset, fd_set *writeset, 
           fd_set *exceptset, const struct timeval *timeout);

參數:
maxfdp1:最大監視文件描述符。
readset:讀事件描述符集。
writeset:寫事件描述符集。
exceptset:異常事件描述符集。
timeout:等待時間。

關於timeout這個參數有以下三中可能:
(1)永遠等待:僅在有一個描述符準備好I/O時才返回。只需將該參數設置爲空指針。
(2)等待一段固定時間:在有一個I/O描述符準備好I/O時或設定的時間到達時返回。
(3)根本不等待:檢查描述符後立即返回(即輪詢),則該參數必須指向一個爲0的timeval的結構體。

關於readset,writeset,expectset三個參數:
FD_ZERO(fd_set *fdset); //將指定的文件描述符集清空
FD_SET(int fd, fd_set *fdset);//在文件描述符集合添加一個新的文件描述符
FD_CLR(int fd, fd_set *fdset);//在文件描述符集合中刪除一個文件描述符
FD_ISSET(int fd, fd_set *fdset);//測試指定的文件描述符是否在該集合中

select函數修該由指針指readset,writeset,expectset所指向的描述符集,因而這三個參數都是值-結果參數。調用該函數時,我們指定所關心的描述符的值,該函數返回時,結果將指示哪些描述符已就緒。

關於maxfdp1:
該參數指定待測試的描述符個數,它的值是待測時的最大描述數的值+1。

該函數的返回值表示跨所有描述符集合的已就緒的總位數。如果在任何描述符就緒之前,定時器先到,則返回0。錯誤則返回-1。

void str_cli(FILE *fp, int sockfd)
{
    int maxfdp1;
    fd_set rset;
    char sendline[MAXLINE], recvline[MAXLINE];
    FD_ZERO(&rset);
    while(1) 
    {
        FD_SET(fileno(fp), &rset);
        FD_SET(sockfd, &rset);
        maxfdp1 = ((fileno(fp) > sockfd) ? fileno(fp) : sockfd) + 1;
        select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) 
        {
            if (read(sockfd, recvline, MAXLINE) == 0)
            { 
                printf("str_cli: recvline terminated prematurely\n");
                exit(0);
            }
            fputs(recvline, stdout);
        }

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

shutdown函數

終止網絡連接的常用方法時調用close函數。不過close有兩個限制,卻可以使用shutdown來避免。
(1)close把描述的引用計數減一,僅在該計數變爲0時才關閉套結字。而使用shutdown可以不管引用計數就激發TCP的正常連接終止序列。
(2)close終止讀和寫兩個方向的數據傳遞。既然TCP是全雙工的,有時候我們需要告知對端我們已經完成了數據發送,即使對端仍有數據要發送給我們。

    #include <sys/socket.h>
    int shutdown(int sockfd, int howto);

該函數的行爲依賴於howto參數的值。
SHUT_RD: 關閉連接的這一半———套結字中不再有數據可接收,而且套結字接受緩衝區中的現有數據都被丟棄。進程不能再對這樣的套結字調用任何讀函數。對一個TCP套結字這樣調用shutdown函數後,由該套結字接收的來自對端的任何數據都被確認,然後悄悄丟棄。

SHUT_WR: 關閉連接的寫這一半———對於TCP套結字,這被稱爲半關閉。當前留在套結字發送緩衝區的數據將被髮送掉,後跟TCP的正常連接終止序列。進程不能再對這樣的套結字調用任何寫函數。

SHUT_RDWR: 連接讀半部和寫半部都關閉———相當於對上面兩個參數一起調用。

void str_cli(FILE *fp, int sockfd)
{
    int maxfdp1;
    fd_set rset;
    char sendline[MAXLINE], recvline[MAXLINE];
    int stdineof = 0;

    FD_ZERO(&rset);
    while(1) 
    {
        FD_SET(fileno(fp), &rset);
        FD_SET(sockfd, &rset);
        maxfdp1 = ((fileno(fp) > sockfd) ? fileno(fp) : sockfd) + 1;
        select(maxfdp1, &rset, NULL, NULL, NULL);

        if (FD_ISSET(sockfd, &rset)) 
        {
            if (read(sockfd, recvline, MAXLINE) == 0)
            { 
                if(stdineof == 1) 
                    return;
                else {
                    printf("str_cli: recvline terminated prematurely\n");
                    exit(0);
                }
            }
            fputs(recvline, stdout);
        }

        if (FD_ISSET(fileno(fp), &rset))
        {
            if (fgets(sendline, MAXLINE, fp) == NULL)
            {
                stdineof = 1;
            shutdown(sockfd, SHUT_WR);
                FD_CLR(fileno(fp), &rset);
                continue;
            }
            write(sockfd, sendline, MAXLINE);
        }
        bzero(recvline, MAXLINE);
        bzero(sendline, MAXLINE);
    }
}   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章