前言
批量處理是指將原先的輸入重定向到一個輸入檔案,這樣客戶端將連續向伺服器傳送該檔案中的資料,然後接收到伺服器的回射資料後,再將其寫入到另一個檔案中。在這樣的情況下,原來的客戶端程式不能夠再正確執行了。那麼會發生什麼問題?我們又該如何修改客戶端程式才能使之正確工作呢?
且看下文。
發現問題一 傳輸線路中資料的丟失
先來看看原客戶端中的資料處理函式的一段:
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 }