误区

Keep Alive指定连接最大空闲时间T,当客户端检测到连接空闲时间超过T时,必须向Broker发送心跳报文PINGREQ,Broker收到心跳请求后返回心跳响应PINGRESP。若Broker超过1.5T时间没收到心跳请求则断开连接,并且投递遗嘱消息到订阅方;同样,若客户端超过一定时间仍没收到心跳响应PINGRESP则断开连接。

首先反驳一下这个误区:

为什么MQTT单独再设计一套心跳机制,而不是使用TCP已经有的心跳机制?

MQTT协议是基于TCP的一个应用层协议,理论上TCP协议在丢失连接时会通知上层应用,但是TCP有一个半打开连接的问题(half-open connection)。这里不打算深入分析TCP协议,需要记住的是,在这种状态下一端的TCP连接已经失效,但是另外一端并不知情,它认为连接依然是打开的,它需要很长的时间(一般情况是两个TLS)才能感知到对端连接已经断开了,这种情况在使用移动或者卫星网络的时候尤为常见。

所以,仅仅依赖TCP层的连接状态监测是不够的,于是MQTT协议设计了一套Keep Alive机制。在MQTT建立连接的时,可以传递一个Keep Alive参数,它的单位为秒,占用两个字节(最大值65535),MQTT协议中约定:在 1.5*Keep Alive时间间隔内,如果MQTT代理服务器没有收到来自Client的任何数据包,那么代理服务器Broker认为它和 Client之间的连接已经断开;同样如果Client没有收到来自Broker的任何数据包,那么Client认为它和Broker之间的连接已经断开。

3.12 PINGREQ – 心跳请求

客户端发送PINGREQ报文给服务端的。 用于:

1. 在没有任何其它控制报文从客户端发给服务的时, 告知服务端客户端还活着。

2. 请求服务端发送 响应确认它还活着。

3. 使用网络以确认网络连接没有断开。

保持连接( Keep Alive) 处理中用到这个报文, 详细信息请查看 3.1.2.10节。

在没有任何其它控制报文从客户端发给服务器时,告知服务器端,客户端还活着。这句话的正确理解是:

如果客户端只有下行数据,没有上行数据,此时虽然不存在链路空闲,但是客户端必须按照心跳周期时间,向服务器端发送心跳请求!

举个例子:

如果客户端订阅了一个Qos 0 的消息(没有其他业务,客户端仅订阅主题消息),并且服务器端每间隔一段时间 t (t 小于心跳周期)向客户端发送客户端订阅的主题消息, 这种情况下客户端必须按照向服务器发送心跳请求。

简介

保持连接Keep Alive(心跳时间)

Bit 7 6 5 4 3 2 1 0
byte 9 保持连接 Keep Alive MSB
byte 10 保持连接 Keep Alive LSB

下面是MQTT关于Keep Alive的官方说明:

如果保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻,到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其他的控制报文可以发送,那么客户端必须发送一个PINGREQ的报文。(客户端负责保证控制报文发送的时间间隔,不超过保持连接的值。如果没有任何其他的控制报文可以发送,客户端必须发送一个PINGREQ报文。)

不管保持连接的值是多少,客户端在任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。

如果保持连接的值非零,并且服务端在1.5倍的保持连接时间内没有收到客户端的控制报文PINGREQ,它必须断开客户端的网络连接,认为网络已经断开。(如果保持连接的值非零, 并且服务端在一点五倍的保持连接时间内没有收到客户端的控制报文, 它必须断开客户端的网络连接, 认为网络连接已断开。)

客户端发送了PINGREQ报文之后,如果在合理时间内没有收到PINGRESP报文,它应该关闭发哦服务端的网络连接。

保持连接的值为零,表示关闭保持连接功能。这意味着服务端不需要因为客户的不活跃而断开连接。注意不管保持连接的值是多少,任何时候,只要服务端认为客户端不是活跃状态,或者无响应的,可以断开客户端的连接。

非规范评注

保持连接的实际值是由应用指定的, 一般是几分钟。 允许的最大值是18小时12分15秒。

通俗理解:

心跳时间(Keep Alive),是一个以秒为单位的时间间隔,表示为一个16位的字。心跳时间指的是,客户端向代理服务器发送一个心跳包 PINGREQ开始计时,到下一次再发送心跳包 PINGREQ时,这中间的时间是一个完整的心跳时间。客户端发送的心跳包 PINGREQ,在链路空闲状态下连续发送时,每次发送的时间间隔不能超过心跳时间Keep Alive。如果客户端没有订阅,发布等操作时,链路处于空闲状态,那么客户端必须发送一个心跳包 PINGREQ,给代理服务器。

不管心跳时间 Keep Alive的值是多少,客户端在任何时候都可以发送心跳包 PINGREQ报文(但是实际测试中,链路有其他报文时,不会有心跳包),客户端使用心跳响应包PINGRESP是客户端用来判断网络和服务器活动状态的,同时,代理服务器是使用客户端发送的心跳包 PINGREQ,来判断网络状态和客户端的活跃状态。

心跳时间间隔keep Alive的值非零时,代理服务器如果在1.5T内没有收到客户的心跳包 PINGREQ,那么代理服务器有权断开当前的MQTT连接,并认为客户端与自己(MQTT代理服务器)网络已经断开。(这一点需要区分用户场景:

  • 如果代理服务器的MCU程序会踢掉MQTT,那么不管标准的MQTT3.1.1版本如何规定,MQTT连接一定会断开。

  • 另外有的客户MCU程序会设定比较长的心跳时间,也可能会等待两个心跳时间判断是否收到了PINGREQ。

  • 还有就是网络底层的问题,MQTT协议仅保证MQTT这一层的实现,对于下面一层的TCP没有任何约束,TCP链路异常,那么MQTT也可能随之关闭。

不同的用户设置不一定相同,但是主流的设定是1.5倍心跳时间。

客户端发送了心跳包 PINGREQ报文之后,如果在合理时间内没有收到PINGRESP报文,客户端应该关闭到服务端的 MQTT连接(客户端应该断开 MQTT连接,但实际上客户端不一定会选择关闭与代理服务器的连接)。

如果心跳时间的值为零,表示关闭心跳超时保持连接功能。这意味着服务端不需要因为客户的不活跃而断开连接。注意不管保持连接的值是多少,任何时候,只要服务端认为客户端不是活跃状态,或者无响应的,可以断开客户端的连接。(有待验证,不同的使用场景,客户端,代理服务器选择的策略都不一样)

PINGREQ — 心跳请求

PINGREQ报文固定报头

Bit 7 6 5 4 3 2 1 0
byte 1 MQTT控制报文类型 (12) 保留位
1 1 0 0 0 0 0 0
byte 2 剩余长度 (0)
0 0 0 0 0 0 0 0
Bit 7 6 5 4 3 2 1 0
byte 1 MQTT控制报文类型 (12) 保留位
1 1 0 0 0 0 0 0
byte 2 剩余长度 (0)
0 0 0 0 0 0 0 0

可变报头:PINGREQ报文没有可变报头。
有效载荷:PINGREQ报文没有有效载荷。
响应       :服务端必须发送 PINGRESP报文响应客户端的PINGREQ报文。
PINGREQ 数据包没有可变头(Variable header)和消息体(Payload),包大小是2字节(是TCP payload的内容是2字节)。当 Client 在一个 Keep Alive 时间间隔内没有向 Broker 发送任何数据包,比如 PUBLISH 和 SUBSCRIBE 的时候,它应该向 Broker 发送 PINGREQ 数据包。

客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。心跳频率在CONNECT可变头部“Keep Alive timer”中定义时间,单位为秒,无符号16位short表示。

若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接(仅供参考选择,非强制规范)。

PINGRESP – 心跳响应
服务端发送 PINGRESP报文,响应客户端发送的 PINGREQ报文。 表示服务端还活着。保持连接( Keep Alive) 处理中用到这个报文。

Bit 7 6 5 4 3 2 1 0
byte 1 MQTT控制报文类型 (13) 保留位
1 0 1 0 0 0 0 0
byte 2 剩余长度 (0)
0 0 0 0 0 0 0 0

可变报头:PINGRESP报文没有可变报头。
有效载荷:PINGRESP报文没有有效载荷。
PINGRESP 数据包没有可变头(Variable header)和消息体(Payload),包大小是2字节(是TCP payload的内容是2字节)。当 Broker 收到来自 Client 的 PINGREQ 数据包,它应该回复 Client 一个 PINGRESP 数据包。

代理服务器Broker,一般若在1.5倍的心跳周期内接收不到客户端发送的PINGREQ,可考虑关闭客户端的连接描述符。此时的关闭连接的行为和接收到客户端发送DISCONNECT消息的处理行为一致,但对客户端的订阅不会产生影响(不会清除客户端订阅数据),这个需要牢记。

若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接(仅供参考选择,非强制规范)。

实际代码具体做法:

在建立连接的时候,客户端可以传递一个 Keep Alive 参数,它的单位为秒,MQTT协议中约定:在 1.5 * Keep Alive 的时间间隔内,如果代理服务器 Broker 没有收到来自客户端 Client 的任何数据包,那么代理服务器 Broker 认为它和客户端 Client 之间的连接已经断开;同样地, 如果客户端 Client 没有收到来自代理服务器 Broker 的任何数据包,那么客户端 Client 认为它和代理服务器 Broker 之间的连接已经断开。

对于 Keep Alive 机制,我们还需要记住以下几点:

  • 如果在一个 Keep Alive 时间间隔内,Client 和 Broker 有过数据包传输,比如 PUBLISH,Client 就没有必要再使用 PINGREQ 了,在网络资源比较紧张的情况下这点很重要;
  • Keep Alive 值是由 Client 指定的,不同的 Client 可以指定不同的值;
  • Keep Alive (占用两个字节)最大值为 18 小时 12 分 15 秒;
  • Keep Alive 值如果设为 0 的话,代表不使用 Keep Alive 机制。

问题:链路空闲多久后,客户端才会向代理服务器发送心跳包PINGREQ?

1、如果是Qos大于0的发布,或者是订阅,取消订阅等,在这些报文发送(交互过程)完成之后,经过一个心跳时间Keep Alive,心跳机制才会去发送心跳请求包 PINGREQ。

2、如果客户端只是订阅了,那么就是从客户端没有上行控制报文开始,经过一个心跳周期,客户端需要向服务器端发送PINGREQ,想客户端证明自己还活着。

问题:如果因为网络问题,客户端发送的心跳包PINGREQ,不能发送到移动网络中去,那么代理服务器会怎么处理?

这个要分情况,网络问题本身就很复杂:

1、客户端的底层网络有问题,客户端写入到了TCP缓冲区内,并且也写成功了,但是底层TCP网络有问题,没有成功将 MQTT心跳请求包 PINGREQ发送给代理服务器,这样代理服务器就不回复心跳响应包PINGRESP。这种情况MQTT链路,可能会在客户端,或者服务器端断开,二者都有权利断开连接。

2、网络有问题,客户端已经把心跳包 PINGREQ发送到了移动网络中去,但是客户端没有收到,如果是连续出现这样的情况,MQTT 链路也会断开。

3、如果是发送了TCP重传,对就是客户端的心跳请求包 PINGREQ,发生了TCP重传,

重传成功了:那么客户端的心跳请求包 PINGREQ可能会超时,这需要看代理服务器如何处理;

重传失败了:那么客户端就会发起TCP层的 [ RST  ACK ],重置TCP链路,MQTT也就随之断开。

问题:如果因为网络问题,代理服务器没有回复心跳响应PINGRESP,客户端会怎么处理?

如果代码里面有对心跳包的标记,比如全局变量pre_ping, pre_ping_response,ping_flag在每次发送心跳之前,都检查上次发送的心跳请求包 PINGREQ 有没有收到心跳响应包 PINGRESP 。通过变量判断控制,那么客户端 Client 可以自行选择是否断开 MQTT 链路,还是重连 MQTT。

MQTT心跳 Keep Alive相关推荐

  1. Mqtt ----心跳机制 长链接 ping

    Mqtt ----心跳机制 心跳机制 Keep Alive指定连接最大空闲时间T,当客户端检测到连接空闲时间超过T时,必须向Broker发送心跳报文PINGREQ,Broker收到心跳请求后返回心跳响 ...

  2. 【MQTT基础篇(十四)】MQTT心跳机制

    文章目录 MQTT心跳机制 MQTT心跳机制 在医院里,医生利用心跳来判断患者是否还有生命体征.对于MQTT服务器来说,它要判断一台MQTT客户端是否依然保持连接可以检查这台客户端是不是经常发送消息给 ...

  3. MQTT 心跳和keepalive配置

    MQTT 心跳和keepalive配置 内容: 正常MQTT 服务器端会配置一个超时时间,一般为60s, 在这个时间段内一个连接如果没有数据传输的话,服务端会主动断开连接以释放资源, 有两种方式可以规 ...

  4. MQTT 基础--Keep Alive和客户接管 - 第 10 部分

    在这篇文章中,我们将讨论 MQTT 的Keep Alive功能以及为什么该功能对移动网络特别重要. TCP连接半开问题 MQTT 基于传输控制协议 (TCP) .该协议确保数据包以"可靠.有 ...

  5. MQTT心跳机制介绍

    在医院里,医生利用心跳来判断患者是否还有生命体征.对于MQTT服务器来说,它要判断一台MQTT客户端是否依然保持连接可以检查这台客户端是不是经常发送消息给服务端.如果经常收到客户端的消息,那么没问题, ...

  6. Mqtt ----心跳机制

    心跳机制 Keep Alive指定连接最大空闲时间T,当客户端检测到连接空闲时间超过T时,必须向Broker发送心跳报文PINGREQ,Broker收到心跳请求后返回心跳响应PINGRESP.若Bro ...

  7. 【如何构建商业级别聊天系统】 MQTT 篇(五)保活 Keep Alive,请不要让你的 MQTT 服务变成小猪佩奇!

    [如何构建商业级别聊天系统] MQTT 篇(五)保活 Keep Alive,请不要让你的 MQTT 服务变成小猪佩奇! 特关人上人!dying 搁浅 神秘连接 哥哥姐姐弟弟妹妹叔叔阿姨们~ 说点闲话 ...

  8. 手机控制的esp8266利用mqtt协议接入百度云智能插座

    手机控制的esp8266利用mqtt协议接入百度云智能插座 19年的春节,相信大家和我一样都待在家里,利用在家的时间现学现卖,制作了一款手机控制的智能插座,网上资料很多,我在查询资料中发现,esp82 ...

  9. Web技术(七):如何使用并实现MQTT 消息订阅-发布模型?

    文章目录 一.什么是发布-订阅消息模型? 二.订阅-发布消息模型有哪些应用? 2.1 应用于IP 物联网络中的消息传递 2.2 应用于操作系统进程间的消息传递 2.3 应用于MESH 自组网中的消息传 ...

最新文章

  1. Jerry眼中的SAP客户数据模型
  2. Java的赋值与初始化
  3. matlab GUI gca gco gcf
  4. pytorch 入门基础
  5. VMware Workstation Pro 16 安装教程
  6. 工作190:页面数据不显示
  7. mysql55条_mysql学习笔记一
  8. 好的程序员在通过单行道时会查看两边
  9. 学了python可以干嘛-学 Python 都用来干嘛的?
  10. C# Udp测试工具开发
  11. matlab如何求空间一点到直线距离,空间点到直线距离怎么求
  12. 宝付国际一文读懂:跨境电商的外汇风险敞口(三)
  13. win10重装系统后Mysql环境和数据的恢复(无需重装Mysql)
  14. 宁波实训day1: java web开发常用工具安装
  15. 第7周编程题在线测试
  16. 廖金菊——湖南高尔夫旅游职业学院文化社诗人
  17. java数组列表_java – 如何显示数组列表中的所有元素?
  18. StudyJams-第01课_初识Android的View(TextView、ImageView、Button)
  19. 计算机无法识别硬盘怎么办,硬盘电脑不识别怎么办?硬盘数据怎么恢复?
  20. JAVA和SQL中时间的格式化 (yyyy-MM-dd HH:mm:ss转换规则)知识总结

热门文章

  1. 新时期智慧环保体系建设详情分析
  2. 一无所有的人怎么创业?一个案例教你如何创业!
  3. POJ 3984 迷宫问题 BFS DFS两种解法
  4. 数字化校园建设规划方案
  5. 考研计算机统考考点,大家帮忙.有没有09年计算机考研统考大纲-之操作系统考点分析 爱问知识人...
  6. Android 自动点击工具,自动点击器app下载-自动点击工具 安卓版v1.0.2-PC6安卓网
  7. SDRAM、DDR SDRAM 学习笔记
  8. Istio 大咖说第 7 期直播预告:基于 Envoy/Istio 的云原生 API 网关——开源项目 Hango 的设计与实现...
  9. 签了工作之后才发现,自己太草率了.....  很长很真实!但会对你有所帮助的!
  10. 2023款 联想小新 Pro 14 和 Thinkbook 14 +区别对比评测