第十八篇:批量處理情況下的回射客戶端

穆晨發表於2017-05-19

前言

       批量處理是指將原先的輸入重定向到一個輸入檔案,這樣客戶端將連續向伺服器傳送該檔案中的資料,然後接收到伺服器的回射資料後,再將其寫入到另一個檔案中。在這樣的情況下,原來的客戶端程式不能夠再正確執行了。那麼會發生什麼問題?我們又該如何修改客戶端程式才能使之正確工作呢?

       且看下文。

發現問題一 傳輸線路中資料的丟失

       先來看看原客戶端中的資料處理函式的一段:

 1 if (FD_ISSET(sockfd, &rset)) {    
 2             if (Readline(sockfd, recvline, MAXLINE) == 0)
 3                 err_quit("str_cli: server terminated prematurely");
 4             Fputs(recvline, stdout);
 5         }
 6 
 7         // 處理使用者輸入
 8         if (FD_ISSET(fileno(fp), &rset)) { 
 9             if (Fgets(sendline, MAXLINE, fp) == NULL)
10                 return;        
11             Writen(sockfd, sendline, strlen(sendline));
12         }

       可以發現,當使用者輸入EOF後,該函式會立刻返回到主程式並執行 close() 關閉連線。但現在問題出現了:在批量處理的情況下,此時傳輸線路中的一部分資料還沒有到達客戶端。而客戶端已經關閉了連線,這部分資料只能被丟棄了。

解決思路

       在當使用者輸入EOF後,我們不立刻斷開連線,而是隻斷開連線的寫入部分( 使用shutdown函式實現 )。只有當伺服器也關閉了服務程式之後,才讓客戶端關閉連線( 為此需要設定一個標記符 stdioeof )。

發現問題二 IO異常

       請再看上述程式碼的第 9 行,在批量情況下,這個輸入語句只會從輸入檔案讀取一行資料就返回,而不理會輸入緩衝區中剩下的資料。上面第2行程式碼也是的。而當回到select以後,即使studio緩衝區仍然有資料沒被取出。它假定是不知道studio使用了緩衝區,故不會返回檔案描述符就緒。

解決思路

       廢棄以文字行為中心的程式碼,改為針對緩衝區操作。

客戶端str_cli () 函式再修訂版

 1 #include    "unp.h"
 2 
 3 void
 4 str_cli(FILE *fp, int sockfd)
 5 {
 6     int            maxfdp1, stdineof;
 7     fd_set        rset;
 8     char        buf[MAXLINE];
 9     int        n;
10 
11     stdineof = 0;
12     FD_ZERO(&rset);
13     for ( ; ; ) {
14         // 當客戶端輸入並沒有結束
15         if (stdineof == 0)
16             FD_SET(fileno(fp), &rset);
17         FD_SET(sockfd, &rset);
18         maxfdp1 = max(fileno(fp), sockfd) + 1;
19         Select(maxfdp1, &rset, NULL, NULL, NULL);
20         if (FD_ISSET(sockfd, &rset)) {    
21             // 注意已經換成了以緩衝區為處理單位的函式,後面的也是。
22             if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
23                 // 當客戶端輸入結束並且伺服器也關閉了服務就正常退出
24                 if (stdineof == 1)
25                     return;        
26                 // 當客戶端輸入還沒結束但伺服器已經關閉服務就正常退出
27                 else
28                     err_quit("str_cli: server terminated prematurely");
29             }
30 
31             Write(fileno(stdout), buf, n);
32         }
33 
34         if (FD_ISSET(fileno(fp), &rset)) { 
35             if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {
36                 stdineof = 1;
37                 // 輸入結束的話關閉連線的寫部分且清空檔案監聽描述符,停止對該描述符的監聽。
38                 Shutdown(sockfd, SHUT_WR);    
39                 FD_CLR(fileno(fp), &rset);
40                 continue;
41             }
42 
43             Writen(sockfd, buf, n);
44         }
45     }
46 }

相關文章