上述这篇文章http://blog.csdn.net/songshimvp1/article/details/51838990中,第4点,以批量处理方式运行客户,仅仅使用select无法实现TCP的半关闭——给服务器发送一个FIN,告诉它我们完成了数据的发送,但是仍然保持套接字描述符打开以便读取。
1、shutdown函数的作用
(1)close把描述符的引用计数减1,仅在该计数变为0时才关闭套接字。使用shutdown可以不管引用计数就激发TCP正常的连接终止序列;
(2)close终止读和写两个方向的数据传送。既然TCP连接是全双工的,有些时候我们需要告知对端我们已完成数据的发送,即使对端还仍有数据要发给我们。在上篇文章中批量输入时调用shutdown将如图:
2、shutdown函数原型定义
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
//shutdown函数的行为依赖于参数howto
/*
howto:
(1)SHUT_RD:关闭连接读这一半;
(2)SHUT_WR:关闭连接写这一半;
(3)SHUT_RDWR:连接的读半部和写半部都关闭(相当于先调用SHUT_RD,再调用SHUT_WR);
*/
3、shutdown函数的使用
(1)使用select目的:只要服务器关闭它那一端的连接就通知我们(我们可以立即获取到服务器关闭的通知);
(2)使用shutdown目的:正确地处理批量输入;
(3)废弃以文本行,改而针对缓冲区操作;
#include "unp.h"
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); // 对应于标准I/O的fp指针
FD_SET(sockfd, &rset); // 对应于套接字scokfd
maxfdp1 = max(fileno(fp), sockfd) + 1;//计算上面两个描述符的最大值
Select(maxfdp1, &rset, NULL, NULL, NULL); //阻塞于select,timeout为NULL,阻塞到某个描述符就绪为止
//可以随时获知服务器是否关闭
if (FD_ISSET(sockfd, &rset)) { /* socket is readable */ //如果套接字可读
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { //读回回射文本
if (stdineof == 1)
return; /* normal termination */
else
err_quit("str_cli: server terminated prematurely");
}
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 */ //调用shutdown,关闭写,发送FIN
FD_CLR(fileno(fp), &rset);
continue;
}
Writen(sockfd, buf, n);
}
}
}
(把这段代码和上一篇文章第四点中的代码对比,你会发现很多东西)