tcp客户/服务器回射程序之五-----用shutdown函数解决在批量方式下所引起的问题
当我们把标准输出和标准输入重定向到文件来运行新的客户程序时,却发现输出文件总是小于输入文件(而对于回射服务器而言,它们应该相等)。问题的起因在于我们对于EOF的处理:str_cli函数就此返回到main函数,而main函数随后终止。然而在批处理方式下,标准输入中的EOF并不意味着我们同时也完成了从套接口的读入;可能仍有请求在去往服务器的路上,或者仍有应答在返回客户的路上。
我们需要的是一种关闭tcp连接其中一半的方法。也就是说我们想给服务器发送一个FIN,告诉它我们已经完成了数据发送,但是仍然保持套接口描述字打开以便读取。这时候,我们可以用shutdown函数来完成。
终止网络连接的通常的方法是调用close函数。但是close函数有两个限制,却可以用shutdown函数来避免。
1).close把描述字的引用计数减1,仅在该计数变为0时才关闭套接口。而使用shutdown可以不管引用计数就激发tcp的正常连接终止的序列。
2).close终止数据传送的两个方向:读和写。
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
//返回:0--成功,-1---出错
howto参数的值:
SHUT_RD:关闭连接的读这一半-----套接口中不再有数据可接收,而且套接口接收缓冲区中的现有数据都被丢弃。进程不能再对这样的套接口调用任何的读函数。对一个tcp套接口这样调用shutdown函数后,由该套接口接收到的来自对端的任何数据都被确认,然后悄然丢弃。
SHUT_WR:关闭连接的写这一半-----对于tcp套接口,这称为半关闭(half-close).当前留在套接口发送缓冲区中的数据将被发送掉,后接tcp的正常连接终止序列。不管套接口描述字的引用计数是否为0,这样的写这一半关闭都照样进行。进程不能再对这样的套接口调用任何的写函数。
SHUT_RDWR:相当于第一次调用指定SHUT_RD,第二次调用指定SHUT_WR.
#define MAXLINE 500
int max(int a, int b) {return a>b?a:b;}
void str_cli(FILE *fp, int connfd) {
//select
int maxfdp, n, stdineof;
fd_set rset;
char buf[MAXLINE];
FD_ZERO(&rset);
stdineof = 0;
for(;;) {
if(0 == stdineof)
FD_SET(fileno(fp), &rset);
FD_SET(connfd, &rset);
maxfdp = max(fileno(fp), connfd) + 1;
select(maxfdp, &rset, NULL, NULL, NULL);
if(FD_ISSET(connfd, &rset)) {
if((n = read(connfd, buf, MAXLINE)) == 0) {
if(stdineof == 1) return;
else {perror("str_cli:server terminated.");return;}
}
write(fileno(stdout), buf, n);
}
if(FD_ISSET(fileno(fp), &rset)) {
if((n = read(fileno(fp), buf, MAXLINE)) == 0) {
stdineof = 1;
shutdown(connfd, SHUT_WR);
FD_CLR(connfd, &rset);
continue;
}
write(connfd, buf, n);
}
}
}