• 为什么 muduo 的 shutdown() 没有直接关闭 TCP 连接?


    陈硕 (giantchen_AT_gmail)

    Blog.csdn.net/Solstice

    Muduo 全系列文章列表: http://blog.csdn.net/Solstice/category/779646.aspx

    今天收到一位网友来信:

    在 simple 中的 daytime 示例中,服务端主动关闭时调用的是如下函数序列,这不是只是关闭了连接上的写操作吗,怎么是关闭了整个连接?

       1: void DaytimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
       2: {
       3:   if (conn->connected())
       4:   {
       5:     conn->send(Timestamp::now().toFormattedString() + "\n");
       6:     conn->shutdown();
       7:   }
       8: }
       9:  
      10: void TcpConnection::shutdown()
      11: {
      12:   if (state_ == kConnected)
      13:   {
      14:     setState(kDisconnecting);
      15:     loop_->runInLoop(boost::bind(&TcpConnection::shutdownInLoop, this));
      16:   }
      17: }
      18:  
      19: void TcpConnection::shutdownInLoop()
      20: {
      21:   loop_->assertInLoopThread();
      22:   if (!channel_->isWriting())
      23:   {
      24:     // we are not writing
      25:     socket_->shutdownWrite();
      26:   }
      27: }
      28:  
      29: void Socket::shutdownWrite()
      30: {
      31:   sockets::shutdownWrite(sockfd_);
      32: }
      33:  
      34: void sockets::shutdownWrite(int sockfd)
      35: {
      36:   if (::shutdown(sockfd, SHUT_WR) < 0)
      37:   {
      38:     LOG_SYSERR << "sockets::shutdownWrite";
      39:   }
      40: }

    陈硕答复如下:

    Muduo TcpConnection 没有提供 close,而只提供 shutdown ,这么做是为了收发数据的完整性。

    TCP 是一个全双工协议,同一个文件描述符既可读又可写, shutdownWrite() 关闭了“写”方向的连接,保留了“读”方向,这称为 TCP half-close。如果直接 close(socket_fd),那么 socket_fd 就不能读或写了。

    用 shutdown 而不用 close 的效果是,如果对方已经发送了数据,这些数据还“在路上”,那么 muduo 不会漏收这些数据。换句话说,muduo 在 TCP 这一层面解决了“当你打算关闭网络连接的时候,如何得知对方有没有发了一些数据而你还没有收到?”这一问题。当然,这个问题也可以在上面的协议层解决,双方商量好不再互发数据,就可以直接断开连接。

    等于说 muduo 把“主动关闭连接”这件事情分成两步来做,如果要主动关闭连接,它会先关本地“写”端,等对方关闭之后,再关本地“读”端。练习:阅读代码,回答“如果被动关闭连接,muduo 的行为如何?” 提示:muduo 在 read() 返回 0 的时候会回调 connection callback,这样客户代码就知道对方断开连接了。

    Muduo 这种关闭连接的方式对对方也有要求,那就是对方 read() 到 0 字节之后会主动关闭连接(无论 shutdownWrite() 还是 close()),一般的网络程序都会这样,不是什么问题。当然,这么做有一个潜在的安全漏洞,万一对方故意不不关,那么 muduo 的连接就一直半开着,消耗系统资源。

    完整的流程是:我们发完了数据,于是 shutdownWrite,发送 TCP FIN 分节,对方会读到 0 字节,然后对方通常会关闭连接,这样 muduo 会读到 0 字节,然后 muduo 关闭连接。(思考题:在 shutdown() 之后,muduo 回调 connection callback 的时间间隔大约是一个 round-trip time,为什么?)

    另外,如果有必要,对方可以在 read() 返回 0 之后继续发送数据,这是直接利用了 half-close TCP 连接。muduo 会收到这些数据,通过 message callback 通知客户代码。

    那么 muduo 什么时候真正 close socket 呢?在 TcpConnection 对象析构的时候。TcpConnection 持有一个 Socket 对象,Socket 是一个 RAII handler,它的析构函数会 close(sockfd_)。这样,如果发生 TcpConnection 对象泄漏,那么我们从 /proc/pid/fd/ 就能找到没有关闭的文件描述符,便于查错。

    muduo 在 read() 返回 0 的时候会回调 connection callback,然后把 TcpConnection 的引用计数减一,如果 TcpConnection 的引用计数降到零,它就会析构了。

    参考:

    《TCP/IP 详解》第一卷第 18.5 节,TCP Half-Close。

    《UNIX 网络编程》第一卷第三版第 6.6 节, shutdown() 函数。

  • 相关阅读:
    java开发_图片截取
    一位台湾软件工程师的心路历程
    WCF Basic(2)服务契约继承
    状态模式(State Pattern)
    .NET Remoting Basic(9)上下文(CallContext)
    访问者模式(Visitor Pattern)
    命令模式(Command Pattern)
    .NET Remoting Basic(10)创建不同宿主的客户端与服务器端
    责任链模式(Chain of Responsibility Pattern)
    消息队列(Message Queuing)
  • 原文地址:https://www.cnblogs.com/Solstice/p/1965342.html
Copyright © 2020-2023  润新知