陈硕 (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)
<!--CRLF-->
2: {
<!--CRLF-->
3: if (conn->connected())
<!--CRLF-->
4: {
<!--CRLF-->
5: conn->send(Timestamp::now().toFormattedString() + "\n");
<!--CRLF-->
6: conn->shutdown();
<!--CRLF-->
7: }
<!--CRLF-->
8: }
<!--CRLF-->
9:
<!--CRLF-->
10: void TcpConnection::shutdown()
<!--CRLF-->
11: {
<!--CRLF-->
12: if (state_ == kConnected)
<!--CRLF-->
13: {
<!--CRLF-->
14: setState(kDisconnecting);
<!--CRLF-->
15: loop_->runInLoop(boost::bind(&TcpConnection::shutdownInLoop, this));
<!--CRLF-->
16: }
<!--CRLF-->
17: }
<!--CRLF-->
18:
<!--CRLF-->
19: void TcpConnection::shutdownInLoop()
<!--CRLF-->
20: {
<!--CRLF-->
21: loop_->assertInLoopThread();
<!--CRLF-->
22: if (!channel_->isWriting())
<!--CRLF-->
23: {
<!--CRLF-->
24: // we are not writing
<!--CRLF-->
25: socket_->shutdownWrite();
<!--CRLF-->
26: }
<!--CRLF-->
27: }
<!--CRLF-->
28:
<!--CRLF-->
29: void Socket::shutdownWrite()
<!--CRLF-->
30: {
<!--CRLF-->
31: sockets::shutdownWrite(sockfd_);
<!--CRLF-->
32: }
<!--CRLF-->
33:
<!--CRLF-->
34: void sockets::shutdownWrite(int sockfd)
<!--CRLF-->
35: {
<!--CRLF-->
36: if (::shutdown(sockfd, SHUT_WR) < 0)
<!--CRLF-->
37: {
<!--CRLF-->
38: LOG_SYSERR << "sockets::shutdownWrite";
<!--CRLF-->
39: }
<!--CRLF-->
40: }
<!--CRLF-->
陈硕答复如下:
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() 函数。
分享到:
相关推荐
本项目应用了muduo网络库、nginx的TCP负载均衡、中间件reids的发布订阅功能、数据库连接池(自制),实现了集群部署聊天服务器 本项目应用了muduo网络库、nginx的TCP负载均衡、中间件reids的发布订阅功能、数据库连接...
基于重写的muduo网络库,并可工作于nginx tcp负载均衡环境的集群聊天服务器,使用数据库连接池访问数据库 基于重写的muduo网络库,并可工作于nginx tcp负载均衡环境的集群聊天服务器,使用数据库连接池访问数据库 ...
基于muduo库实现了工作在nginx tcp负载均衡环境中的集群聊天服务器和客户端,采用redis消息队列进行集群,mysql数据库存储 基于muduo库实现了工作在nginx tcp负载均衡环境中的集群聊天服务器和客户端,采用redis消息...
基于nginx tcp负载均衡 + redis的集群聊天服务器,网络层使用muduo库搭建,数据库使用MySQL 基于nginx tcp负载均衡 + redis的集群聊天服务器,网络层使用muduo库搭建,数据库使用MySQL 基于nginx tcp负载均衡 + ...
网络服务器要处理的对象无外乎,监听者TcpServer,连接接收者Acceptor,连接者TcpConnection等等主体对象。要处理的事件有监听者可写,连接者读写等主体事件。宏观上面,所有的连接可以看作成一个通道Channel,通道...
基于muduo库实现的集群聊天服务器,通过mysql存储数据,通过nginx实现tcp负载均衡,通过redis实现集群内服务器间的消息订阅发布。 基于muduo库实现的集群聊天服务器,通过mysql存储数据,通过nginx实现tcp负载均衡,...
基于muduo网络库的集群聊天服务器和客户端源码,使用nginx tcp负载均衡,mysql 基于muduo网络库的集群聊天服务器和客户端源码,使用nginx tcp负载均衡,mysql数据库,redis发布-订阅数据库,redis发布-订阅 基于...
集群聊天服务器(nginx tcp负载均衡模块、muduo网络库、基于发布-订阅的redis消息队列、mysql数据库) 集群聊天服务器(nginx tcp负载均衡模块、muduo网络库、基于发布-订阅的redis消息队列、mysql数据库) 集群聊天...
可以工作在nginx tcp负载均衡环境中的集群聊天服务器和客户端源码 基于muduo库实现 redis用于服务器中间件即消息队列 mysql用于保存用户数据 可以工作在nginx tcp负载均衡环境中的集群聊天服务器和客户端源码 基于...
基于muduo库开发的集群聊天服务器和客户端源码,可以工作在nginx tcp负载均衡环境中,使用mysql数据库,运用了json的序列化和反序列化 基于muduo库开发的集群聊天服务器和客户端源码,可以工作在nginx tcp负载均衡...
一个工作在nginx_tcp中的集群聊天服务器,数据库用mysql部署,网络通信采用muduo和json消息作为通信协议 一个工作在nginx_tcp中的集群聊天服务器,数据库用mysql部署,网络通信采用muduo和json消息作为通信协议 一个...
muduo 是一个基于 Reactor 模式的现代 C 网络库,它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。视频连接:...
完整版muduo网络库,仅供学习使用
《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...
Muduo_网络库使用手册
muduo 是一个基于 Reactor 模式的现代 C++ 网络库,它采用非阻塞 IO 模型,基于事件驱动和回调,原生支持多核多线程,适合编写 Linux 服务端多线程网络应用程序
muduo是一个高性能的网络中间件,支持TCP协议。本文说明如何将其移植到嵌入式linux上,以及如何使用的过程。
个人在阅读muduo源码时,做的注释,非常详细,可以配合我的博客一起阅读。
用来分析muduo的reactor结构,怕自己忘了,写个思维导图总结一下