12 | 连接无效:使用Keep-Alive还是应用心跳来检测?

2019-08-28 盛延敏
《网络编程实战》
课程介绍


讲述:冯永吉

时长:大小11.14M


你好,我是盛延敏,这里是网络编程实战第 12 讲,欢迎回来。
上一篇文章中,我们讲到了如何使用 close 和 shutdown 来完成连接的关闭,在大多数情况下,我们会优选 shutdown 来完成对连接一个方向的关闭,待对端处理完之后,再完成另外一个方向的关闭。
在很多情况下,连接的一端需要一直感知连接的状态,如果连接无效了,应用程序可能需要报错,或者重新发起连接等。
在这一篇文章中,我将带你体验一下对连接状态的检测,并提供检测连接状态的最佳实践。

从一个例子开始

让我们用一个例子开始今天的话题。
我之前做过一个基于 NATS 消息系统的项目,多个消息的提供者 (pub)和订阅者(sub)都连到 NATS 消息系统,通过这个系统来完成消息的投递和订阅处理。
突然有一天,线上报了一个故障,一个流程不能正常处理。经排查,发现消息正确地投递到了 NATS 服务端,但是消息订阅者没有收到该消息,也没能做出处理,导致流程没能进行下去。
通过观察消息订阅者后发现,消息订阅者到 NATS 服务端的连接虽然显示是“正常”的...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 传说中的成大大
    置顶
    2019-08-28
    思考题 1. udp不需要连接 所以没有必要心跳包 2. 我觉得还是很有必要判定存活 像以前网吧打游戏 朋友的电脑突然蓝屏死机 朋友的角色还残留于游戏中,所以服务器为了判定他是否真的存活还是需要一个心跳包 隔了一段时间过后把朋友角色踢下线

    作者回复: 2是一个很好的例子。

    共 5 条评论
    19
  • fjpcode
    2019-08-28
    1.UDP里面各方并不会维护一个socket上下文状态是无连接的,如果为了连接而保活是不必要的,如果为了探测对端是否正常工作而做ping-pong也是可行的。 2.额外的探活报文是会占用一些带宽资源,可根据实际业务场景,适当增加保活时间,降低探活频率,简化ping-pong协议。 3.多次探活是为了防止误伤,避免ping包在网络中丢失掉了,而误认为对端死亡。

    作者回复: 👍

    共 2 条评论
    48
  • 扩散性百万咸面包
    2020-04-17
    想到HTTP Header也能设置Connection: Keep-Alive,也是应用层协议,是不是底层实现也类似于定时器+Ping Pong的思路?

    作者回复: HTTP的 keep-alive是为了使http变成长连接,在此前的http 1.0中,每次http的请求-响应之后,tcp连接就会被释放掉,这显然是非常浪费的,于是通过加入keep-alive,使得http连接不会被立即释放。

    共 2 条评论
    18
  • 云师兄
    2019-12-17
    文章中提到保活有两个方向,实际应用中,会有两个方向同时探测的场景吗

    作者回复: 当然有。服务器端要探活client来保证自己不会维护无效连接,客户端来探活保持自己是不是可以持续申请资源。

    
    8
  • 满怀
    2019-12-20
    老师 我想问一下 看您在回复当中有说 虽然TCP本身的keep-alive机制可以设置保活时间,保活探测时间间隔以及探测次数,但是应用层会无法感知,那么这种情况下会怎么处理呢 就是文中所给出的三种情况吗

    作者回复: 是的,大部分的应用程序开发者都会选择自己在应用层处理连接有效性的检测。

    
    7
  • 徐凯
    2019-09-06
    老师 同步连接可以实现心跳包么 如果不能的话 那同步连接如果因为客户端崩溃 没有通过四次挥手结束连接 服务端还堵塞在接收数据 那么这样如何判断对方已经离开呢

    作者回复: 首先,客户端应用程序崩溃是可以有FIN包的,如果有的话,read阻塞就可以返回了;其次,如果真的是客户端机器跪了,那么是没有FIN包发出的,这个时候,我们只好一直在那里傻等。 且慢,还有别的辙,那就是不要用阻塞I/O,不要在哪里傻傻等待。使用I/O复用就可以办到的,往后看就会明白了。

    共 3 条评论
    6
  • rongyefeng
    2020-05-18
    服务器端恢复心跳包这段 case MSG_PING: { messageObject pong_message; pong_message.type = MSG_PONG; sleep(sleepingTime); ssize_t rc = send(connfd, (char *) &pong_message, sizeof(pong_message), 0); 老师,这个type是int类型,应该将其转为网络序才对吧? pong_message.type = htonl(MSG_PONG); 而且你的客户端程序也是有转换的,是不是服务器端这里忘记了?

    作者回复: Bingo。确实是我忘记了:)

    
    4
  • HunterYuan
    2019-12-17
    对于协议栈中的TCP的keep-alive是可以手动配置的,全局配置通过修改net.ipv4下的等参数;局部配置可以通过setsockopt修改socket选项。在工作中遇到过一个oracle,windows服务器的保活时间大于,我们设备状态连接表的保活时间,导致,一段时间后重连,导致服务器报错问题。当时最最快的处理办法是,修改window默认的保活时间小于状态连接失效时间。

    作者回复: 学习了。

    
    4
  • 石将从
    2019-08-29
    为啥这句套接字要加1呢?int rc = select(socket_fd + 1, &readmask, NULL, NULL, &tv);

    作者回复: 这是因为,嗯,跟select实现有关,我再后面讲select时详细剖析,现在先记住吧。

    共 4 条评论
    4
  • LDxy
    2019-08-28
    TCP本身的keep-alive的时间是可以自己设置的吗?如果是可以自己设置的,为何还需要自己实现这个机制?

    作者回复: 可以设置的,但是有时候应用层需要感知处理这样的异常

    
    3