• 安全关闭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() 函数关闭套接字。
  • 相关阅读:
    乙二醇循环水除酸技术
    Hazelcast系列(八):数据结构
    vue学习笔记
    红色Cy5 NHS生物相容性与溶解性1263093-76-0
    老太太阿姨收割机秀才被封
    Empowering Low-Light Image Enhancer through Customized Learnable Priors 论文阅读笔记
    当SCM遇见RPA:实现高效协调的供应链管理
    艾美捷内毒素纯化树脂成员说明和特点介绍
    【C++ 科学计算】C++ 预测算法之多项式曲线拟合
    MPI学习笔记(二):矩阵相乘的两种实现方法
  • 原文地址:https://blog.csdn.net/weixin_45419466/article/details/138536407