批量输入
客户端发送数据后,需要等待一段时间才能收到服务器回复信息。这段时间是RTT(往返时间,round-trip time,RTT)加上服务器处理的时间。
假设RTT为8个时间,应答时刻在时刻4发出,时刻7接收到。之前程序的停-等通信方式如下图所示。
由于管道是全双工的,所以在这种停-等方式中,仅仅使用了管道容量的1/8。在交互式输入中是合适的。但是如果用文件代替标准输入,会发现请求和回复的不对等。
这是批量方式造成的。在批量传输的情况下,时刻7管道是充满的。假设我们的请求连续不断地发送,一直发送到最后一个EOF。
在传统的套接字通信中,客户端读写函数在写到请求的EOF时,就会返回main函数,而main函数随后终止。然而在批量输入中,标准输入的EOF并不代表我们完成了套接字的读入。可能仍然有请求在去往服务器的路上,也或者仍然有应答在返回客户端的路上。
可以使用shutdown函数解决这个问题。
shutdown函数
(1)shutdown函数和close函数的区别
使用close函数终止连接有两个限制:
a、close函数仅仅是将引用计数减1,直到减为0才关闭套接字。
b、close会终止读和写两个方向的数据传递。对于全双工的TCP连接而言,可能存在漏数据的问题。
使用shutdown函数好处:
a、不用管引用计数,直接激发TCP的正常连接终止序列(发送FIN)
b、可以只关闭读或者只关闭写。
(2)函数原型
#include<sys/socket.h>
int shutdown(int sockfd, int howto);
(3)int howto参数
SHUT_RD:
关闭连接的读这一半。套接字中不会再有数据可以接收,且套接字缓冲区中的现有数据也全部丢弃。进程不能再对这个套接字调用任何的读函数。
SHUT_WR:
关闭连接的写这一半。对于TCP套接字而言,这被称为半关闭。当前留在套接字缓冲区的数据会被全部发送掉。进程不能再对该套接字调用任何写函数。
SHUT_RDWR:
读和写的半部都关闭。
修改客户端IO函数
修改思路。使用系统文件IO代替标准stdio的IO函数。解决缓冲区问题。
读入全部用read,写出全部用write。避免使用用户缓冲区。
同时,为了避免批量输入输出出错,使用shutdown函数来关闭写和读。设置写标记位write_end。如果写入结束,则将write_end置为1,再shutdown。
void str_cli(FILE *fp, int sockfd){
int maxfdp1, stdineof;
fd_set rset;
char buf[MAXLINE];
int n;
stdineof = 0;
FD_ZERO(&rset);
for ( ; ; ) {
if (stdineof == 0)
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + 1;
Select(maxfdp1, &rset, NULL, NULL, NULL);
/*读半部*/
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
if (stdineof == 1)
return; /* normal termination */
else{
printf(“连接异常断开!”);
exit(1);
}
}
Write(fileno(stdout), buf, n);
}
/*写半部*/
if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */
if ( (n = Read(fileno(fp), buf, MAXLINE)) == 0) {
stdineof = 1;
Shutdown(sockfd, SHUT_WR); /* send FIN */
FD_CLR(fileno(fp), &rset);
continue; //使用continue,继续循环,但是不检测写半部了
}
Writen(sockfd, buf, n);
}
}
}