安全关闭Tcp连接

close与shutdwon

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
}

Tcp连接错误关闭示例

依旧采取上面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

接收的数据回归正常。

安全关闭TCP的流程

  • 发送方不再发送数据后,使用 shutdown(sock,SHUT_WR) 关闭本端套接字的输出流。shutdown() 会向对方发送 FIN 包。FIN 包通过四次挥手过程断开连接,可以有效的等待数据发送完成再断开连接。
  • 调用 read() 函数,read()将会返回0,代表对方也不再发送数据。此时连接已断开。
    (这里read()返回0应考虑客户端存在Bug或恶意的不返回0的情况,使得客户端永远不满足read()=0的情况。因此这里因考虑有超时机制,在shutdown之后若干秒内如果没有满足read()=0,则强制断开连接并有相应的错误处理)
  • 调用 close() 函数关闭套接字。
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值