UNP-UNIX網絡編程 第十六章:非阻塞式I/O

(一) 概述

套接字默認狀態是阻塞的。也就是說當發出一個不能立即完成的套接字調用時,進程將被投入睡眠,等待相應操作的完成 ,一般分爲四類:
1. 輸入操作:包括read、readv、recv、recvfrom、recvmsg這5個函數。當套接字緩衝區沒數據可讀時,進程將被投入睡眠狀態,直到有數據可達。
2. 輸出操作:包括write、writev、send、snedto和sendmsg這5個函數。
如果發送緩衝區沒有空間,則進程將被投入睡眠,因爲這些操作需要將應用進程緩衝區的數據拷貝到套接字的發送緩衝區的數據
(UDP不存在真正的發送緩衝,內核只是複製數據並把他們沿着協議棧向下傳送並依次包裝UDP首部和IP首部)。
3. 接收連接:accept函數,這個就很熟悉了,當服務器等待客戶端外來連接的到來時將被投入睡眠。
4. 建立連接:根據一個TCP三路握手的過程,connect函數至少阻塞一個RTT的時間(自己的SYN發送和服務器的ACK的接收)。

(二)非阻塞str_cli函數

主要複雜之處在於緩衝區的管理,設想一個場景:
如果標準輸入可讀,read返回,接着調用write,然而,如果這個時候套接字發送緩衝區已經滿了,則write會阻塞,當write阻塞的時間裏面,很有可能有來自接收緩衝區的數據可讀;

1.複雜版本,使用select+非阻塞I/O
(1).fcntl將標準輸入、標準輸出和網絡套接字描述符都設置爲非阻塞模式。
(2)調用select之前對4中情況進行判斷。
(3).四種套接字可讀或者可寫後的操作,細節先忽略。

2.簡單版本,使用fork多線程
函數一開始就把進程劃分爲父進程和子進程,父進程用來把標準輸入的文本複製到服務器,子進程用來將回射的文本複製到標準輸出。
從圖中可以看到,儘管只有一個套接字(父子進程共享),但由於TCP是全雙工的,並且這個套接字有兩個描述符(fork子進程時完成拷貝),所以父進程可以往裏寫,而子進程可以從中讀。
這裏需要注意一些邊界條件,例如服務器關閉,客戶子進程將收到EOF,此時子進程需要給父進程一個SIGTERM以防止父進程仍在執行。
另外,父進程中要用shutdown而不是close,因爲此時是多進程共享描述符,引用計數大於1。

#include    "unp.h"
void str_cli(FILE *fp, int sockfd)
{
    pid_t   pid;
    char    sendline[MAXLINE], recvline[MAXLINE];

    if ( (pid = Fork()) == 0) {     /* child: server -> stdout */
        while (Readline(sockfd, recvline, MAXLINE) > 0)
            Fputs(recvline, stdout);

        kill(getppid(), SIGTERM);   /* in case parent still running */
        exit(0);
    }

        /* parent: stdin -> server */
    while (Fgets(sendline, MAXLINE, fp) != NULL)
        Writen(sockfd, sendline, strlen(sendline));

    Shutdown(sockfd, SHUT_WR);  /* EOF on stdin, send FIN */
    pause();
    return;
}

注:客戶向服務器複製兩千行文本所用時間:
select+阻塞I/O:12.3 s;
select+非阻塞I/O:6.9 s;
fork多線程:8.7 s;
多線程:8.5 s。

(三)非阻塞connect

當在一個非阻塞的套接字上調用connect的時候,會立刻返回一個EINPROGRESS錯誤,不過TCP三路握手連接將繼續進行。當調用connect後,我們可以使用select檢測這個連接成功、失敗。非阻塞connect主要有三個用途:
1. 三路握手的同時進行其他處理;
2. 同時建立多個連接,例如web服務器;
3. 減少connect的超時等待時間;
利用非阻塞的connect可以構建一個web客戶端,例如我們在與一個web服務器建立一個http連接的時候,我們會獲取一個homepage,隨後這個主頁往往還有對其他網頁的引用,客戶可以通過非阻塞的connect並行地獲取多個網頁,而不是阻塞地串行獲取,這樣就提高了效率。(考慮到TCP的擁塞避免機制,他對網絡是不利的)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章