http://blog.csdn.net/Solstice/article/details/6208634

今天收到一位网友来信:

在 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() 函数。

muduo 的 shutdown() 没有直接关闭 TCP 连接?的更多相关文章

  1. HTTP的RST包与WinHttp延迟关闭TCP连接

    一.RST包也常见于断开TCP连接  几个月前用wireshark抓HTTP包发现有的网络通信在结束的时候没有使用四次握手,而是直接使用RST包.如: 在TCP协议中RST表示复位,用来异常的关闭连接 ...

  2. golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期

    欢迎访问我的个人网站获取更佳阅读排版 golang 网络编程之如何正确关闭tcp连接以及管理它的生命周期 | yoko blog (https://pengrl.com/p/47401/) 本篇文章部 ...

  3. Linux 内核协议栈之TCP连接关闭

    Close行为: 当应用程序在调用close()函数关闭TCP连接时,Linux内核的默认行为是将套接口发送队列里的原有数据(比如之前残留的数据)以及新加入 的数据(比如函数close()产生的FIN ...

  4. TCP连接的关闭

    原文地址:http://lib.csdn.net/article/computernetworks/17264   TCP连接的关闭有两个方法close和shutdown,这篇文章将尽量精简的说明它们 ...

  5. 我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景

    欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 写在前面..... 本文是笔者肉眼盯 Bug 系列的第三弹,前 ...

  6. 7.1.1.关闭WebSocket连接

    7.1.定义 7.1.1.关闭WebSocket连接 为_关闭WebSocket连接_,端点需关闭底层TCP连接.端点应该使用一个方法完全地关闭TCP连接,以及TLS会话,如果合适,丢弃任何可能已经接 ...

  7. close_wait状态和time_wait状态(TCP连接)

    1.CLOSE_WAIT的简单解决方案 不久前,我的Socket Client程序遇到了一个非常尴尬的错误.它本来应该在一个socket长连接上持续不断地向服务器发送数据,如果socket连接断开,那 ...

  8. TCP系列07—连接管理—6、TCP连接管理的状态机

            经过前面对TCP连接管理的介绍,我们本小节通过TCP连接管理的状态机来总结一下看看TCP连接的状态变化 一.TCP状态机整体状态转换图(截取自第二版TCPIP详解) 二.TCP连接建立 ...

  9. 简述TCP连接的建立与释放(三次握手、四次挥手)

    在介绍TCP连接的建立与释放之前,先回顾一下相关知识. TCP是面向连接的运输层协议,它提供可靠交付的.全双工的.面向字节流的点对点服务.HTTP协议便是基于TCP协议实现的.(虽然作为应用层协议,H ...

随机推荐

  1. js 判断日期大小、是否在时间范围内等处理

    var beginval="2015-09-01";//这个时间可以是日期控件选择的,也可以是其他的任何日期时间 var endval="2015-09-01" ...

  2. MySQL用户密码过期登陆报错ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

    今天接到主从复制失败告警,查看MySQL,发现MySQL能够登陆但是执行命令报错, ERROR 1820 (HY000): You must reset your password using ALT ...

  3. poj2447

    题意:两个素数P,Q.N=P*Q; T=(P-1)*(Q-1); (E*D)mod T = 1; (0<=D<T).E与T互质,公钥是{E,N},私钥是{D,N}.原始信息M的加密过程为C ...

  4. 浅谈js设计模式之发布 — 订阅模式

    发布 — 订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在 JavaScript开发中,我们一般用事件模型来替代传统的发布 — ...

  5. LeetCode(16):最接近的三数之和

    Medium! 题目描述: 给定一个包括 n 个整数的数组 nums 和 一个目标值 target.找出 nums 中的三个整数,使得它们的和与 target 最接近.返回这三个数的和.假定每组输入只 ...

  6. Redis持久化存储(RDB和AOF)

    参考了: https://blog.csdn.net/canot/article/details/52886923  和 https://www.cnblogs.com/zhangchao-letv/ ...

  7. CVE-2010-0249 IE8 UAF漏洞分析

    CVE-2010-0249 [CNNVD]Microsoft Internet Explorer非法事件操作内存破坏漏洞(CNNVD-201001-153) Microsoft Internet Ex ...

  8. LOJ 10160 - 「一本通 5.2 练习 3」周年纪念晚会 / 没有上司的晚会

    题面 传送门 Ural 州立大学的校长正在筹备学校的 8080 周年纪念聚会.由于学校的职员有不同的职务级别,可以构成一棵以校长为根的人事关系树.每个资源都有一个唯一的整数编号,从 $1$ 到 $N$ ...

  9. Luogu P1549 棋盘问题(2)

    题意 在N×N的棋盘上(1≤N≤10),填入1,2,-,N^2,共N^2个数,使得任意两个相邻的数之和为素数. 思路 先线性筛(非标准版),然后用a数组记录以i为下标的数是不是质数(就是标记数组),然 ...

  10. js时间格式化函数(兼容IOS)

    * 时间格式化 * @param {Object} dateObj 时间对象 * @param {String} fmt 格式化字符串 */ dateFormat(dateObj, fmt) { le ...