第十八篇:批量处理情况下的回射客户端

前言

       批量处理是指将原先的输入重定向到一个输入文件,这样客户端将连续向服务器发送该文件中的数据,然后接收到服务器的回射数据后,再将其写入到另一个文件中。在这样的情况下,原来的客户端程序不能够再正确运行了。那么会发生什么问题?我们又该如何修改客户端程序才能使之正确工作呢?

       且看下文。

发现问题一 传输线路中数据的丢失

       先来看看原客户端中的数据处理函数的一段:

 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 }
posted @ 2017-05-19 13:52  穆晨  阅读(285)  评论(0编辑  收藏  举报