第十七篇:IO複用之select實現

穆晨發表於2017-05-19

前言

       在看過前文:初探IO複用後,想必你已對IO複用這個概念有了初步但清晰的認識。

       接下來,我要在一個具體的併發客戶端中實現它(基於select函式),使得一旦伺服器中的客戶程式被終止的時候,客戶端這邊立即得到通知並返回異常。

select函式

函式原型:int select ( int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout )

包含標頭檔案:sys/select.h  sys/time.h ( 兩個都要包含 )

引數說明:maxfdp1表示監聽描述符個數( 一般直接賦予最大描述符號的值+1 ),中間幾個參數列示具體的監聽描述符集( 讀/寫/異常描述符集,本質是位向量組 ),最後一個參數列示監聽的時間限制結構體。

配套使用巨集:

1. void FD_ZERO ( fd_set *fdset ) 監聽描述符集清零

2. void FD_SET ( int fd, fd_set *fset ) 註冊要監聽的描述符

3. void FD_CLR ( int fd, fd_set *fdset ) 登出不要監聽的描述符

4. int FD_ISSET ( int fd, fd_set *fdset ) 判斷某個監聽訊號是否接收到( 一般用於在select函式呼叫後某個訊號是否接收到判斷 )

返回值:返回已經就緒的描述符的個數,如果是定時器到時則返回0,出錯則返回-1。

大致用法:

1. 定義一個空的描述符集

以下部分迴圈:

2. 註冊要監聽的描述符

3. 呼叫select函式

4. 依次編寫IO處理程式碼( 一般是if ( FD_ISSET(描述符), &監聽描述符集 ) { } 這樣的結構 )。

程式碼實現

       下面的程式碼對之前的客戶端程式碼做了修改,增加了基於select函式的IO複用機制:

 1 #include    "unp.h"
 2 
 3 void
 4 str_cli(FILE *fp, int sockfd)
 5 {
 6     int            maxfdp1;
 7     // 定義描述符集
 8     fd_set        rset;
 9     char        sendline[MAXLINE], recvline[MAXLINE];
10 
11     // 清空描述符集
12     FD_ZERO(&rset);
13     for ( ; ; ) {
14         // 註冊要監聽的兩個描述符
15         FD_SET(fileno(fp), &rset);
16         FD_SET(sockfd, &rset);
17         // 計算最大描述符並將它+1後作為select函式的第一個引數
18         maxfdp1 = max(fileno(fp), sockfd) + 1;
19         // 呼叫並阻塞於select函式
20         Select(maxfdp1, &rset, NULL, NULL, NULL);
21 
22         // 讀取回射以及處理TCP分節
23         if (FD_ISSET(sockfd, &rset)) {    
24             if (Readline(sockfd, recvline, MAXLINE) == 0)
25                 err_quit("str_cli: server terminated prematurely");
26             Fputs(recvline, stdout);
27         }
28 
29         // 處理使用者輸入
30         if (FD_ISSET(fileno(fp), &rset)) { 
31             if (Fgets(sendline, MAXLINE, fp) == NULL)
32                 return;        
33             Writen(sockfd, sendline, strlen(sendline));
34         }
35     }
36 }

執行測試

       經過測試,發現當伺服器殺死客戶端子程式後,客戶端這邊立刻報錯並退出了程式。

相關文章