网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

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

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

从一个例子开始

让我们用一个例子开始今天的话题。
我之前做过一个基于 NATS 消息系统的项目,多个消息的提供者 (pub)和订阅者(sub)都连到 NATS 消息系统,通过这个系统来完成消息的投递和订阅处理。
突然有一天,线上报了一个故障,一个流程不能正常处理。经排查,发现消息正确地投递到了 NATS 服务端,但是消息订阅者没有收到该消息,也没能做出处理,导致流程没能进行下去。
通过观察消息订阅者后发现,消息订阅者到 NATS 服务端的连接虽然显示是“正常”的,但实际上,这个连接已经是无效的了。为什么呢?这是因为 NATS 服务器崩溃过,NATS 服务器和消息订阅者之间的连接中断 FIN 包,由于异常情况,没能够正常到达消息订阅者,这样造成的结果就是消息订阅者一直维护着一个“过时的”连接,不会收到 NATS 服务器发送来的消息。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(29)

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

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

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

    作者回复: 👍

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

    作者回复: 首先,客户端应用程序崩溃是可以有FIN包的,如果有的话,read阻塞就可以返回了;其次,如果真的是客户端机器跪了,那么是没有FIN包发出的,这个时候,我们只好一直在那里傻等。

    且慢,还有别的辙,那就是不要用阻塞I/O,不要在哪里傻傻等待。使用I/O复用就可以办到的,往后看就会明白了。

    2019-09-06
    1
    3
  • 衬衫的价格是19美元
    tcp探活的目的之一是判断该连接是否还需要,以此决定是维持还是释放该连接资源。udp无连接,也不占用服务器或者客户端的资源,因此业务无需探活
    2019-10-04
    1
  • 我叫徐小晋
    老师您好,这个保活时间是指一个定时器,到了这个时间会发送一个ping消息。
    保活时间间隔是什么?

    作者回复: 保活时间间隔就是每隔多长时间发送类似的ping消息。定时器是保活实现的一种常见形式。

    2019-12-04
  • bbbi
    我的理解是这样的:
    TCP协议并没有提供一个保活的措施,如果出现TCP已经连接后,两端异常崩溃或者是编码问题没有及时的关闭连接(比如客户端发送-->服务端收到消息返回数据-->客户端收到后直接退出,没有调用close),那么服务器会一直维护着这个通道,造成资源浪费。所以加入keepalive,系统提供一个计时器,在约定的时间下发送检测包。但是这个计时器检测时间默认是2小时开始检测时间太长,所以我们可以在应用层,手动发送检测包

    作者回复: 基本是这样。而且应用层处理的时候,还有各种异常处理,便于暴露和发现问题,如果是使用tcp协议的keepalive,不是很容易处理这些细节。

    2019-12-01
    1
  • godtrue
    探测TCP连接是否有效,KEEP-ALIVE可以实现,不过时间太长啦!所以,通常会在应用层,自己仿照KEEP-ALIVE的原理弄一个自己更加可以灵活可控的。
    2019-11-23
  • zhan
    这程序,没看懂。。。

    作者回复: 哪里没看懂?

    2019-10-26
  • 15652790052
    请教一个问题: 客户端如果奔溃了->进程退出了->内核会收到关于此进程的所有资源,包括socket文件->意味这个socket的两个方向都关闭了,怎么会感知不到而傻等呢

    作者回复: 这种情况下确实不会傻等。

    2019-10-08
  • 陈诚
    如果能收到服务器端的应答,则结束保活,将保活时间置为 0。

    --->
    如果能收到服务器端的应答,则结束保活,将保活时间置为KEEP_ALIVE_TIME,即恢复初值
    2019-09-28
  • 有点意思
    老师 你好
    现在做的项目中要用到心跳,具体是这样的
    服务端一个 客户端多个
    每个客户端会持续的往服务端发送从网卡抓取的流量,如果某段时间网卡没有流量就不发 但是连接还需要维持 之前用的keepalive 现在想换成应用层心跳
    想到的方案如下:
    客户端开启线程1来发送抓到的流量 并且每间隔1秒就发送一次心跳给服务端 服务端收到心跳会回复一下
    同时开启另外一个线程2启用select和recv来接收心跳响应 如果在 3秒内没接受到心跳 那么就判断连接断开

    老师你觉得这种方案可以么 有什么缺点么

    作者回复: 每秒一次会不会太快了?

    2019-09-25
    2
  • 冷雨
    第一题:udp可以使用同样的方式
    第二题:确实占用带宽,但一方面ping-pong报文大小比较小,带宽占用有限,另一方面及时确认连接是否可用比带宽的损耗要重要的多。为什么要多次才能确认呢,因为网络抖动可能导致ping消息没有收到或者pong消息没有返回,如果一次就关闭,很容易导致误杀,而且socket建立的成本也比较高,多次确认对应用程序来说也是可以接受的。
    2019-09-11
  • Rancood
    UDP本身就是无连接的,不需要探活;现在带宽可以忽略不计了,有时候可能因为网络原因报文不可达,所以需要多试几次
    2019-09-10
  • 另一半棉花糖
    在对端计算能力紧张、或者网络延迟比较大的时候,仍然会对每一个ping包都做回复,但是本端可能在发了第KEEP_ALIVE_PROBETIMES-1个包之后才收到对端针对第一个ping做的回复。这样虽然本端就会认为这个连接是有效的,但会在接下来接收到好几个pong包;如果这一批的某一个pong包的到来时间正好落在本端第二次由于select超时而进行链路探测的时间段内的话,则可能产生“对端实际已经down了,但是本端过很久才能检测到(可能要达到N个select超时时间)”的问题。这个问题该怎么解决?要给ping和pong加序列号以用于匹配么?

    作者回复: 这个是你工作中碰到过的,还是你自己构想出来的案例?

    本质上,你问的问题是网络有延迟,导致检测时间也有延迟,我觉得这个是完全正常的,因为我们的程序是"企图"发现对端是否down掉了,是一个后知后觉型的,肯定是需要时间判断的。一般我们可以通过设置这个N的值来进行调整。比如N=3,三个包不回就认为down掉了。

    2019-09-09
  • Smirk
    占带宽那个问题忙时不用探活包,正常通讯本身可以判是不是活,闲时探活包数据量比正常通讯包小很多。
    2019-09-09
  • wyf2317
    1 udp不需要
    2 9102年了,这点带宽不成问题。反倒是保持连接带来的一系列资源消耗才是重点。增加心跳检查可以快速确认客户端是否存活或者客户端快速重连。减少资源消耗,提升用户体验。

    作者回复: 哈哈,9102年。

    2019-09-05
  • 苦行僧
    使用心跳包不就是为了保持keep alive吗?文章内容到最后也没有点题?

    作者回复: 好吧,我应该加上你说的"点题"的。我在总结里加了一点。

    2019-09-02
  • 沉淀的梦想
    老师在pingclient的实现中,为什么只要该一下结构体的字段值就能重置探活时间了?

            if (FD_ISSET(socket_fd, &readmask)) {
                //......
                // 重置探活时间
                tv.tv_sec = KEEP_ALIVE_TIME;
            }

    作者回复: 我应该还把heatbeats置为0了。

    2019-09-01
    1
  • 徐凯
    我看到大家对于第二个问题的答案都提到了为了避免探活包丢包 所以要发多个探活包。但是我觉得只要发了探活包对方就一定能收到,就算丢了 发送端也会重传。而大家说的发送多个探活包的原因 是因为重传需要等到计时器超时才传,而如果网络堵塞的话可能会出现频繁丢包 那么服务端可能需要很久才能知道对方已经离开 发多个探活包的话 就减少了这个等待的时间 这样理解对么

    作者回复: 探活包的多次发送,还是为了减少误判的概率,你说的是一种情况,但我觉得多此探活肯定会拉长对一个"无效"连接的判断时间的。这个是一个tradeoff,所以大多数程序都把这个作为选项让使用者自己配置。

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

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

    2019-08-29
    2
收起评论
29
返回
顶部