如果MQTT Client 想接收离线消息:

如果 Client 想接收离线消息,

1.Client 必须使用持久化的会话(Clean Session = 0)连接到 Broker
2. 持久会话能被恢复的前提是客户端使用固定的 Client ID 再次连接,如果 Client ID是动态的,那么连接成功后将会创建一个新的持久会话。

注意:

1.MQTT 3.1.1 没有规定持久会话应该在什么时候过期,如果仅从协议层面理解的话,这个持久会话应该永久存在。但在实际场景中这并不现实,因为它会非常占用服务端的资源,所以服务端通常不会完全遵循协议来实现,而是向用户提供一个全局配置来限制会话的过期时间。
2.MQTT 5.0 中Session Expiry Interval 解决了 MQTT 3.1.1 中持久会话永久存在造成的服务器资源浪费问题。设置为 0 或未设置,表示断开连接时会话即到期;设置为大于 0 的数值,则表示会话在网络连接关闭后会保持多少秒;设置为 0xFFFFFFFF 表示会话永远不会过期。

1. MQTT中的QoS等级

MQTT设计了一套保证消息稳定传输的机制,包括消息应答、存储和重传。在这套机制下,提供了三种不同层次QoS(Quality of Service):

QoS0,At most once,至多一次;
QoS1,At least once,至少一次;
QoS2,Exactly once,确保只有一次。

QoS 是消息的发送方(Sender)和接受方(Receiver)之间达成的一个协议:

QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。
注意:

QoS是Sender和Receiver之间的协议,而不是Publisher和Subscriber之间的协议。换句话说,Publisher发布了一条QoS1的消息,只能保证Broker能至少收到一次这个消息;而对于Subscriber能否至少收到一次这个消息,还要取决于Subscriber在Subscibe的时候和Broker协商的QoS等级。

1.1. QoS0
QoS0等级下,Sender和Receiver之间一次消息的传递流程如下:

Sender向Receiver发送一个包含消息数据的PUBLISH包,然后不管结果如何,丢掉已发送的PUBLISH包,一条消息的发送完成。

1.2. QoS1
QoS1要保证消息至少到达一次,所以有一个应答的机制。Sender和Receiver的一次消息的传递流程如下:

Sender向Receiver发送一个带有数据的PUBLISH包,并在本地保存这个PUBLISH包;
Receiver收到PUBLISH包以后,向Sender发送一个PUBACK数据包,PUBACK数据包没有消息体(Payload),在可变头中有一个包标识(Packet Identifier),和它收到的PUBLISH包中的Packet Identifier一致。
Sender收到PUBACK之后,根据PUBACK包中的Packet Identifier找到本地保存的PUBLISH包,然后丢弃掉,一次消息的发送完成。
但是消息传递流程中可能会出现问题:

如果Sender在一段时间内没有收到PUBLISH包对应的PUBACK,它将该PUBLISH包的DUP标识设为1(代表是重新发送的PUBLISH包),然后重新发送该PUBLISH包。
Receiver可能会重复收到消息,需自行去重。
1.3. QoS2
相比QoS0和QoS1,QoS2不仅要确保Receiver能收到Sender发送的消息,还需要确保消息不重复。它的重传和应答机制就要复杂一些,同时开销也是最大的。QoS2下,一次消息的传递流程如下所示:

Sender发送QoS为2的PUBLISH数据包,数据包 Packet Identifier 为 P,并在本地保存该PUBLISH包;
Receiver收到PUBLISH数据包后,在本地保存PUBLISH包的Packet Identifier P,并回复Sender一个PUBREC数据包,PUBREC数据包可变头中的Packet Identifier为P,没有消息体(Payload);
当Sender收到PUBREC,它就可以安全的丢弃掉初始Packet Identifier为P的PUBLISH数据包。同时保存该PUBREC数据包,并回复Receiver一个PUBREL数据包,PUBREL数据包可变头中的Packet Identifier为P,没有消息体;
当Receiver收到PUBREL数据包,它可以丢掉保存的PUBLISH包的Packet Identifier P,并回复Sender一个可变头中 Packet Identifier 为 P,没有消息体(Payload)的PUBCOMP数据包;
当Sender收到PUBCOMP包,那么认为传输已完成,则丢掉对应的PUBREC数据包;
上面是一次完整无误的传输过程,然而传输过程中可能会出现以下情况:

情况1:Sender发送PUBLISH数据包给Receiver的时候,发送失败;
情况2:Sender已经成功发送PUBLISH数据包给Receiver了,但是Receiver发送PUBREC数据包失败;
情况3:Sender已经成功收到了PUBREC数据包,但是PUBREL数据包发送失败;
情况4:Receiver已经收到了PUBREL数据包,但是发送PUBCOMP数据包时发送失败
针对上述的问题,较为详细的处理方法如下:

不管是情况1还是情况2,因为Sender在一定时间内没有收到PUBREC,那么它会把PUBLISH包的DUP标识设为1,重新发送该PUBLISH数据包;

不管是情况3还是情况4,因为Sender在一定时间内没有收到PUBCOMP包,那么它会重新发送PUBREL数据包;

针对情况2,Receiver可能会收到多个重复的PUBLISH包,更加完善的处理如下:

Receiver在收到PUBLISH数据包之后,马上回复一个PUBREC数据包。并会在本地保存PUBLISH包的Packet Identifier P,不管之后因为重传多少次这个Packet Identifier 为P的数据包,Receiver都认为是重复的,丢弃。同时Receiver接收到QoS为2的PUBLISH数据包后,**并不马上投递给上层,**而是在本地做持久化,将消息保存起来(这里需要是持久化而不是保存在内存)。

针对情况4,更加完善的处理如下:

Receiver收到PUBREL数据包后,正式将消息递交给上层应用层,投递之后销毁Packet Identifier P,并发送PUBCOMP数据包,销毁之前的持久化消息。之后不管接收到多少个PUBREL数据包,因为没有Packet Identifier P,直接回复PUBCOMP数据包即可。
2. QoS降级
在 MQTT 协议中,从 Broker 到 Subscriber 这段消息传递的实际 QoS 等于:Publisher 发布消息时指定的 QoS 等级和 Subscriber 在订阅时与 Broker 协商的 QoS 等级,这两个 QoS 等级中的最小那一个。
3. QoS和会话
如果 Client 想接收离线消息,必须使用持久化的会话(Clean Session = 0)连接到 Broker,这样 Broker 才会存储 Client 在离线期间没有确认接收的 QoS 大于 等于1 的消息。

在发送QoS为1或2的情况,Broker(此时为Sender)会将发送的PUBLISH数据包保存到本地,直到收到一系列回复的数据包,然而Client(此时为Receiver)在离线期间无法回复相应的数据包,所以会一直存储。

  1. QoS等级使用建议
    在以下情况下你可以选择 QoS0:

Client 和 Broker 之间的网络连接非常稳定,例如一个通过有线网络连接到 Broker 的测试用 Client;
可以接受丢失部分消息,比如你有一个传感器以非常短的间隔发布状态数据,所以丢一些也可以接受;
你不需要离线消息。
在以下情况下你应该选择 QoS1:

你需要接收所有的消息,而且你的应用可以接受并处理重复的消息;
你无法接受 QoS2 带来的额外开销,QoS1 发送消息的速度比 QoS2 快很多。
在以下情况下你应该选择 QoS2:

你的应用必须接收到所有的消息,而且你的应用在重复的消息下无法正常工作,同时你也能接受 QoS2 带来的额外开销。

MQTT 持久会话

不稳定的网络及有限的硬件资源是物联网应用需要面对的两大难题,MQTT 客户端与服务器的连接可能随时会因为网络波动及资源限制而异常断开。为了解决网络连接断开对通信造成的影响,MQTT 协议提供了持久会话功能。

MQTT 客户端在发起到服务器的连接时,可以设置是否创建一个持久会话。持久会话会保存一些重要的数据,以使会话能在多个网络连接中继续。持久会话主要有以下三个作用:

避免因网络中断导致需要反复订阅带来的额外开销。
避免错过离线期间的消息。
确保 QoS 1 和 QoS 2 的消息质量保证不被网络中断影响。

持久会话需要存储哪些数据?

通过上文我们知道持久会话需要存储一些重要的数据,以使会话能被恢复。这些数据有的存储在客户端,有的则存储在服务端。

客户端中存储的会话数据:

已发送给服务端,但是还没有完成确认的 QoS 1 与 QoS 2 消息。
从服务端收到的,但是还没有完成确认的 QoS 2 消息。

服务端中存储的会话数据:

会话是否存在,即使会话状态其余部分为空。
已发送给客户端,但是还没有完成确认的 QoS 1 与 QoS 2 消息。
等待传输给客户端的 QoS 0 消息(可选),QoS 1 与 QoS 2 消息。
从客户端收到的,但是还没有完成确认的 QoS 2消息,遗嘱消息和遗嘱延时间隔。

MQTT Clean Session 的使用

Clean Session 是用来控制会话状态生命周期的标志位,为 true 时表示创建一个新的会话,在客户端断开连接时,会话将自动销毁。为 false 时表示创建一个持久会话,在客户端断开连接后会话仍然保持,直到会话超时注销。

注意: 持久会话能被恢复的前提是客户端使用固定的 Client ID 再次连接,如果 Client ID是动态的,那么连接成功后将会创建一个新的持久会话。

MQTT 3.1.1 没有规定持久会话应该在什么时候过期,如果仅从协议层面理解的话,这个持久会话应该永久存在。但在实际场景中这并不现实,因为它会非常占用服务端的资源,所以服务端通常不会完全遵循协议来实现,而是向用户提供一个全局配置来限制会话的过期时间。

比如 EMQ 提供的 免费公共 MQTT 服务器 设置的会话过期时间为 5 分钟,最大消息数为 1000 条,且不保存 QoS 0 消息。
MQTT 5.0 中的会话改进
MQTT 5.0 中将 Clean Session 拆分成了 Clean Start 与 Session Expiry Interval。Clean Start 用于指定连接时是创建一个全新的会话还是尝试复用一个已存在的会话,Session Expiry Interval 用于指定网络连接断开后会话的过期时间。

Clean Start 为 true 时表示必须丢弃任何已存在的会话,并创建一个全新的会话;为 false 时表示必须使用与 Client ID 关联的会话来恢复与客户端的通信(除非会话不存在)。

Session Expiry Interval 解决了 MQTT 3.1.1 中持久会话永久存在造成的服务器资源浪费问题。设置为 0 或未设置,表示断开连接时会话即到期;设置为大于 0 的数值,则表示会话在网络连接关闭后会保持多少秒;设置为 0xFFFFFFFF 表示会话永远不会过期。
关于 MQTT 会话的 Q&A
当会话结束后,保留消息还存在么?

MQTT 保留消息不是会话状态的一部分,它们不会在会话结束时被删除。

客户端如何知道当前会话是被恢复的会话?

MQTT 协议从 v3.1.1 开始,就为 CONNACK 报文设计了 Session Present 字段。当服务器返回的该字段值为 1 时,表示当前连接将会复用服务器保存的会话。客户端可通过该字段值决定在连接成功后是否需要重新订阅。

使用持久会话时有哪些建议?

不能使用动态 Client ID,需要保证客户端每次连接的 Client ID 都是固定的。
根据服务器性能、网络状况、客户端类型等合理评估会话过期时间。设置过长会占用更多的服务端资源,设置过短会导致未重连成功会话就失效。
当客户端确定不再需要会话时,可使用 Clean Session 为 true 进行重连,重连成功后再断开连接。 如果是 MQTT 5.0则可在断开连接时直接设置 Session Expiry Interval 为 0,表示连接断开后会话即失效。

Qos0 vs Qos1 vs Qos2



如果MQTT Client 想接收离线消息,

如果 Client 想接收离线消息,

1.Client 必须使用持久化的会话(Clean Session = 0)连接到 Broker
2. 持久会话能被恢复的前提是客户端使用固定的 Client ID 再次连接,如果 Client ID是动态的,那么连接成功后将会创建一个新的持久会话。

注意:

1.MQTT 3.1.1 没有规定持久会话应该在什么时候过期,如果仅从协议层面理解的话,这个持久会话应该永久存在。但在实际场景中这并不现实,因为它会非常占用服务端的资源,所以服务端通常不会完全遵循协议来实现,而是向用户提供一个全局配置来限制会话的过期时间。
2.MQTT 5.0 中Session Expiry Interval 解决了 MQTT 3.1.1 中持久会话永久存在造成的服务器资源浪费问题。设置为 0 或未设置,表示断开连接时会话即到期;设置为大于 0 的数值,则表示会话在网络连接关闭后会保持多少秒;设置为 0xFFFFFFFF 表示会话永远不会过期。

本文参考
MQTT的QoS介绍
MQTT 持久会话与 Clean Session 详解
Mqtt Qos 深度解读

如果MQTT Client 想接收离线消息相关推荐

  1. 微信接收离线消息状态的设置方法(图文教程)

    为什么80%的码农都做不了架构师?>>>    摘自:穆童博客 http://mtoou.info 相信大家在上QQ的时候应该发现了很多人在使用腾讯的微信了,而且还常常发现使用了微信 ...

  2. uniapp打包app,对接华为厂商,实现unipush离线消息推送

    今天终于可以抽出点时间,来记录一下这几天心塞的心情.上周公司派过来一个活,说是使用uniapp制作一个app,同时要实现在线消息推送和离线消息推送,啥话没说就揽了下来.不过说实在的,从来没有开发过ap ...

  3. Android集成小米华为推送以及收不到离线消息的坑

    华为推送 1.应用被杀后,无法收到透传消息? 应用被杀死后,无法收到透传消息,再手动打开app,也收不到之前发送的消息,像有的第三SDK,应用被杀后,再打开app,还可以收到之前的离线消息,华为推送不 ...

  4. 一种通过xmpp实现离线消息推送的方法及系统

    公开号 : CN 104243271 A 专利申请号 : CN 201310230953 申请人 : 深圳中兴网信科技有限公司 [摘要] 本发明公开了一种通过XMPP实现离线消息推送的方法,在XMPP ...

  5. html消息发送接收,在html页面中 如何应用mqtt协议发送/接收消息

    经过前面几篇文章的介绍,在很多场景下利用NodeMCU加持mqtt协议来控制几乎所有需要传感器监控的行业都能极大地简化物联的成本.在这样一个基础上,还能拓展出很多好玩的.实际运用的甚至能够作为商业化运 ...

  6. 关于个推 vivo 接收不到离线消息问题

    关于个推 vivo 接收不到离线消息问题 在2020年6月1日 vivo对消息推送管控进行调整, 对推送消息分为两种 0:运营消息 1:系统消息 在java 使用个推sdk的情况下 vivo对默认选择 ...

  7. MQTT——具有.NET Core的消息队列遥测传输协议

    目录 介绍 什么是MQTT REST是什么? 体系结构 有效载荷 安全 代码样例 代理 发布者 订阅者 执行 概要 Git存储库链接 参考文献 介绍 发布/订阅模式是解耦软件的常见要求.有各种可用的技 ...

  8. mysql t 保存_检查 (调试) - 离线消息保存到 MySQL - 《EMQ X Enterprise v4.1 中文文档》 - 书栈网 · BookStack...

    离线消息保存到 MySQL 搭建 MySQL 数据库,并设置用户名密码为 root/public,以 MacOS X 为例: $ brew install mysql $ brew services ...

  9. IM开发干货分享:我是如何解决大量离线消息导致客户端卡顿的

    1.引言 好久没写技术文章了,今天这篇不是原理性文章,而是为大家分享一下由笔者主导开发实施的IM即时通讯聊天系统,针对大量离线消息(包括消息漫游)导致的用户体验问题的升级改造全过程. 文章中,我将从如 ...

最新文章

  1. 关于C语言中printf函数“输出歧视”的问题
  2. 基于Ubuntu交叉编译FFmpeg Windows SDK
  3. key php 转小写_PHP代码层防护与绕过
  4. python写入txt文件正常,但是写入csv文件中文乱码问题
  5. Maven安装和配置环境变量
  6. 洛谷3605 Promotion Counting
  7. MySQL 5.7 新特性详解
  8. 启动马达接线实物图_星三角降压启动电路图实物接线图
  9. 真是虚惊一场的i_like_cpp
  10. RocketMQ(七)——消息的消费
  11. Waymo在美国推出自动驾驶汽车共享服务
  12. PHP高级教程-Session
  13. 动词ing基本用法_高中英语:非谓语动词 ( 附非谓语口诀)
  14. YoC RTOS 实战:FOTA系统升级
  15. re模块或正则表达式
  16. c语言编程如何进行n次方运算,c语言n次方怎么输入?_后端开发
  17. 站在巨人的肩膀上--邵泓鑫
  18. 前端展示json格式数组
  19. nginx反向代理非80端口/nginx反代非80端口
  20. 获取中国移动光猫H2-2超管密码

热门文章

  1. shader 反射 水面_UnityShader-菲涅尔反射(Fresnel Reflection)
  2. 回声的来源和消除(转载)
  3. vue中使用qrcode2js插件生成二维码并下载
  4. 电脑桌面上可以显示便签的软件叫什么
  5. MATLAB如何画漂亮的图
  6. 【Windows】之搭建 FTP 服务器
  7. 天冷不可怕,心冷才可怕!!!
  8. DLP数据防泄漏技术(转载)
  9. [论文阅读]Balanced Sparsity for Efficient DNN Inference on GPU
  10. Get新技能Interest