int close(int sockfd);
关闭sokcet,这里注意:当程序调用close关闭socket的时候,如果缓冲区中仍然有数据的话,协议栈会发送RST包代替FIN包,丢弃缓冲的数据,强行关闭连接
int shutdown(int sockfd, int howto);
该函数的行为依赖howto参数的值:
SHUT_RD
关闭读功能,套接字中不再有数据可接收,而且套接字接收缓冲区中的现有数据都被丢弃。
SHUT_WR
关闭写功能,对于TCP套接字,称为半关闭(half-close)。当前在套接字发送缓冲区中的数据将被发送掉,后跟TCP的正常连接终止序列。
SHUT_RDWR
关闭读写功能,等于调用shutdown函数两次,连接的读半部和写半部都关闭。
recipes-master/tpc/sender.cc
void sender(const char* filename, TcpStreamPtr stream)
{
FILE* fp = fopen(filename, "rb");
if (!fp)
return;
printf("Sleeping 10 seconds.\n");
sleep(10);
printf("Start sending file %s\n", filename);
char buf[8192];
size_t nr = 0;
while ( (nr = fread(buf, 1, sizeof buf, fp)) > 0)
{
stream->sendAll(buf, nr);
}
fclose(fp);
printf("Finish sending file %s\n", filename);
// Safe close connection
printf("Shutdown write and read until EOF\n");
stream->shutdownWrite(); // 相当于shutdown(sock,SHUT_WR)
// 读出缓冲区数据直到为0
while ( (nr = stream->receiveSome(buf, sizeof buf)) > 0)
{
// do nothing
}
printf("All done.\n");
// TcpStream destructs here, close the TCP socket.
// 析构函数会调用close关闭socket
}
依旧采取上面sender.cc的程序,我们先把下面几行注释掉
stream->shutdownWrite(); // 相当于shutdown(sock,SHUT_WR)
// 读出缓冲区数据直到为0
while ( (nr = stream->receiveSome(buf, sizeof buf)) > 0)
{
// do nothing
}
正常情况下,sender发送文件,nc正常接收
[wang@localhost tpc]$ ./sender ttcp 12345
Accepting... Ctrl-C to exit
accepted no. 1 client
Sleeping 10 seconds.
Start sending file ttcp
Finish sending file ttcp
Shutdown write and read until EOF
All done.
[wang@localhost tpc]$ nc localhost 12345 | wc -c
1236576
随后,我们在nc起来的时候输入一些数据
[wang@localhost tpc]$ nc localhost 12345 | wc -c
42718472019840918490-1249-01284-01298-04912-03491-0391-03912-03912-0
Ncat: Connection reset by peer.
777764
第一行是我们输入的数据,这些数据会发送给sender,最后一行是我们接收到的数据大小,很明显是比原来的要少的。这是因为sender本身并没有read操作,所以这些输入的数据会滞留在缓冲区,但是sender发送完数据,直接调用close,根据前面对close的介绍,这时候sender会发送一个RST,导致Tcp连接强行断开,所以nc并没有完全接受完数据。
下面我们恢复sender.cc原来的代码,再测试一下
[wang@localhost tpc]$ nc localhost 12345 | wc -c
42718472019840918490-1249-01284-01298-04912-03491-0391-03912-03912-0
1236576
接收的数据回归正常。