前言
在看過前文:初探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 }
執行測試
經過測試,發現當伺服器殺死客戶端子程式後,客戶端這邊立刻報錯並退出了程式。