请点赞关注,你的支持对我意义重大。
???? Hi,我是小彭。本文已收录到 GitHub · AndroidFamily 中。这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] 带你建立核心竞争力。
前言
大家好,我是小彭。
MQTT 是一种基于发布 - 订阅模型的消息传递协议,在物联网和移动应用有较广泛的应用。如果你的目标是冲击中高级工程师岗位,MQTT 或许是一个不错的亮点。最近,我还发现很多候选人会在简历中写自己 “熟悉 MQTT 协议”,但多数人只是停留在了解或用过的程度。
这篇文章里,我将与你探讨 MQTT 协议的 工作原理 & 协议消息格式 & 核心特性,实战的部分我们会在下篇文章中讨论。如果能帮上忙,请务必点赞加关注,给小彭一点创作的动力。
记录:2022 年 9 月 9 日修订:优化文章结构
学习路线图:
1. 认识 MQTT
1.1 什么是 MQTT?
MQTT (Message Queuing Telemetry Transport,消息队列遥测传输) 是一种基于 TCP/IP 协议族的应用层协议。MQTT 协议是专门针对硬件性能低下 & 网络状况不稳定的场景设计的,这使得 MQTT 在物联网和移动应用等受限场景得到广泛应用。
1.2 MQTT 协议的发展历史
- 1999 年:Andy Stanfork-Clark (IBM) 和 Arlen Nipper 发布 MQTT 协议,用于通过卫星连接石油管道遥测系统,MQTT 中的 TT (Telemetry Transport) 就是源于这样一个遥测系统;
- 2010 年:MQTT 协议免费发布;
- 2014 年:MQTT 协议正式成为 OASIS 标准,经过多年的发展,MQTT 协议已经成为互联网 (IoT) 的主要协议之一。
目前,MQTT 主要分为两个大版本:
1.3 MQTT 协议的工作模型
MQTT 是基于发布 - 订阅模型 (pub/sub) 的消息传递协议,与请求 - 响应模型不同,发布 - 订阅模型主要有三种角色:publisher & subscriber & subscriber:
-
publisher & subscriber (发布者 & 订阅者): 是指通过网络连接到 MQTT broker 的设备,也叫 客户端 (client)。一个客户端既可以作为消息发布者,也可以作为消息订阅者;
-
broker (代理): 代理是整个发布 - 订阅模型的核心,也叫 服务端。
当 client 发布某个主题的消息时,broker 会将该消息分发给任何已订阅该主题的 client。通常来说,client 不会存储消息,一旦消息被发送到这些 client,消息就会从 broker 上删除。另外,保留消息、持久连接和服务质量 QoS 可能会导致消息临时存储在 broker 上。
发布 - 订阅模式使得 消息的发布者和订阅者解耦,主要体现为空间解耦和时间解耦:
-
空间解耦 / 设备解耦: 发布者和订阅者通过 broker 进行消息传递,相互之间感知不到对方的存在。当一个客户端断线时,整个系统可以继续工作;
-
时间解耦: publisher 和 subscriber 不一定需要同时运行;
图片引用自 https://juejin.cn/post/6976441705067184135 —— cxuan 著
1.4 MQTT 协议和 HTTP 协议有什么区别?
特性 | MQTT 协议 | HTTP 协议 |
---|---|---|
传输层 | TCP | TCP 或 UDP |
分发模型 | 发布 - 订阅模型 | 请求 - 响应模型 |
分发关系 | 1 对 0/1/N | 1 对 1 |
数据安全 | 使用 SSL/TLS | 不一定采用 HTTPS |
加密 | 应用层对有效载荷加密 | 不在应用层加密 |
消息头大小 | 较小 | 较大 |
-
1、MQTT 协议基于传输层 TCP 协议,而 HTTP 可以基于 TCP 或 UDP(HTTP/3);
-
2、MQTT 协议采用发布 - 订阅模型,同一个设备既可以是发布者也可以是订阅者;而 HTTP 协议采用请求 - 响应模型,一个设备作为请求方,另一个设备作为响应方;
-
3、MQTT 消息分发可以是 1 对 0/1/N,而 HTTP 消息分发是 1 对 1;
-
4、MQTT 协议使用 SSL/TLS 来确保安全,而 HTTP 协议并不规定使用 HTTPS;
-
5、MQTT 协议在应用层对有效载荷 (payloads) 加密,而 HTTPS 协议不在应用层加密,在传输数据前不会加密数据;
-
6、MQTT 消息头较小,而 HTTP 消息头较大(HTTP/2 有头部压缩);
1.5 为什么 MQTT 协议适合物联网和移动应用场景?
物联网和移动应用场景的特点是硬件性能低下和网络状况不稳定,而 MQTT 协议就是专门针对这种环境设计的,主要在四个方面有优势:
-
1、架构设计: MQTT 协议采用发布 - 订阅模型,使得消息发布者和消息订阅者互相解耦,当一个客户端断线时,整个系统可以继续工作。这使得 MQTT 在网络质量的场景下更具优势;
-
2、消息大小: MQTT 协议具有非常小的消息头,这使得 MQTT 协议更适应低带宽网络环境;
-
3、交付能力: MQTT 协议提供了更丰富的消息交付保证能力,它定义了三种消息发布服务质量 (QoS):“最多发一次”、“最少发一次” 和 “正好发一次”。其中,“正好一次” 用于计费系统和 IM App 推送中,能确保用户收到且只收到一次;
-
4、间歇性连接: MQTT 提供了遗嘱消息和保留消息的特性。遗嘱消息使得客户端端断开连接时,所有订阅的客户端都能收到来自代理的消息;保留消息意味着新订阅的客户端可以立即获得保留的消息(类似粘性消息)。这使得 MQTT 协议更适合网络不稳定的 间歇性连接的场景。
1.6 谁更适合物联网(HTTP/2 & WebSocket & MQTT)?
-
HTTP/2 是 HTTP/1.x 的升级,主要体现在:利用 “多路复用和二进制分帧” 来解决队首阻塞问题,降低了通信时延;利用 “头部压缩” 减少消息头部,降低了传输开销;实现了 服务器推送,允许在不发起请求的情况下将数据推送到客户端,弥补了 Http/1.x 依赖 Websockets 才能实现推送的缺陷。这些改进使得 HTTP/2 也具有适应物联网场景的条件;
-
WebSockets 是在 Web 浏览器和 Web 服务器之间进行握手的协议,它降低了使用 Http/1.x 进行双工通信的开销。随着 HTTP/2 成为标准,对 websockets 的需求可能会下降;
-
MQTT 是基于发布订阅模型的协议,因其带宽消耗小而被广泛应用于物联网协议。
结论:这三种协议并没有绝对的优胜者,最好的协议取决于具体的需求和限制条件。但如果只从带宽、电池、功能多样性这些基本条件看,MQTT 在其中是更占优的选择。例如,我司的 IM 产品在 App 端是采用 MQTT 协议的实现,而在 Web 端因为有良好的 WebSocket 能力基础,所以采用的是 WebSocket 传输 MQTT 格式消息。
1.7 为什么 MQTT 协议基于 TCP,可以基于 UDP 协议吗?
MQTT 协议的设计特性中包含了一项 “高可靠性交付”,它需要一个保证可靠的底层传输层协议,因此 TCP 协议、TLS 协议、WebSocket 协议都可能作为 MQTT 的底层协议。而无连接的 UDP 协议会丢失或重排数据,不能满足 MQTT 协议的传输需要。
2. MQTT 协议消息格式
2.1 MQTT 协议消息的特点
-
1、基于二进制: MQTT 是一种基于二进制的协议,所谓基于二进制,是指 MQTT 协议操作的元素是二进制数据而不是文本数据;
-
2、命令 & 命令确认格式: MQTT 消息采用命令 & 命令确认的格式,每个命令消息都有一个关联的命令确认消息,两个消息之间会通过一个 ”包唯一标识“ 字段进行关联???TODO。这与 TCP 的报文确认应答机制是类似的,不过两者的颗粒度是不同的,MQTT 是对整个应用层消息的确认,而 TCP 是对传输层报文段的确认,或者说是对序列号的确认;
-
3、消息头很小: MQTT 消息头最小只需要 2 字节。
2.2 MQTT 协议消息基本结构
一个 MQTT 消息由三部分组成:
MQTT 消息结构 | 描述 | 长度 |
---|---|---|
固定报头(Fixed header) | 存在于所有 MQTT 消息中 | 2 到 5 字节 |
可变报头(Variable header) | 存在于部分 MQTT 消息中 | 0 或 N 字节 |
载荷(Payloads) | 存在于部分 MQTT 消息中 | 0 或 N 字节 |
1、固定报头
所有 MQTT 消息都包含一个固定报头,固定报头由消息类型、标志位和剩余长度三个部分。固定报头长度为 2 ~ 5 字节,具体取决于 “剩余长度(Remaining Length)” 的大小,剩余长度表示当前消息剩余部分的字节数,包括可变报头和有效载荷的长度,但不包括剩余长度字段本身的字节数。
提示: 如何判断剩余长度的字节数,采用的是前缀无歧义的表示法。
固定报头格式如下:
MQTT 消息类型(MQTT Control Packet type)汇总如下:
消息类型 | 值 | 消息流转方向 | 描述 | 需要有效载荷 |
---|---|---|---|---|
Reserved | 0 | 禁止 | 保留 | / |
CONNECT | 1 | => | 客户端请求连接服务器 | ✔ |
CONNACK | 2 | <= | CONNECT 消息确认 | ✖ |
PUBLISH | 3 | <==> | 客户端发布消息 | 可选 |
PUBACK | 4 | <==> | PUBLISH 消息确认(QoS 1) | ✖ |
PUBREC | 5 | <==> | 发布收到(保证交付第一步) | ✖ |
PUBREL | 6 | <==> | 发布释放(保证交付第二步) | ✖ |
PUBCOMP | 7 | <==> | 发布完成(保证交付第三步) | ✖ |
SUBSCRIBE | 8 | => | 客户端订阅消息 | ✔ |
SUBACK | 9 | <= | SUBSCRIBE 消息确认 | ✔ |
UNSUBSCRIBE | 10 | => | 客户端取消订阅 | ✔ |
UNSUBACK | 11 | <= | UNSUBSCRIBE 消息确认 | ✖ |
PINGREQ | 12 | => | 心跳请求 | ✖ |
PINGRESP | 13 | <= | PINGREQ 消息确认 | ✖ |
DISCONNECT | 14 | => | 客户端断开连接 | ✖ |
Reserved | 15 | 禁止 | 保留 | / |
2、可变报头
不同消息的可变报头内容不一样,不过其中有一个比较通用的字段:
- 包唯一标识(Packet Identifier): SUBSCRIBE,UNSUBSCRIBE,PUBLISH(QoS > 0)的消息中会包含一个 2 字节的唯一标识字段,每次 client 发送这些消息时,必须分配一个未使用过的唯一标识。而这些消息的应答消息,如 PUBACK、PUBREC、PUBREL、UNSUBACK 必须与对应消息携带相同的唯一标识。
3、载荷
某些 MQTT 消息会包含一个有效载荷,对于 PUBLISH 消息来说,有效载荷就是应用消息。
3. MQTT 协议消息类型详解
上一节,我们提到在 MQTT 固定报文头部中会标记 MQTT 消息类型(MQTT Control Packet type) ,这一节我们具体讨论下这些消息类型。
3.1 连接消息
MQTT 的连接总是发生在 client 和 broker 之间,两个 client 之间不会互相感知。请求连接时,client 会向 broker 发送 CONNECT
连接消息,broker 接受连接后会响应 CONNACK
连接确认消息。一旦连接建立,连接会一直保持打开状态,直到 client 发送 DISCONNECT
断开连接消息或连接异常中断。
CONNECT 请求连接:
CONNECT
是 client 发送给 broker 的首个消息,并且在一次连接中,client 只能发送一次 CONNECT
消息,发送的第二个 CONNECT
消息会被 broker 当作违反协议处理,并断开连接。在 CONNECT
消息中,主要包含以下内容:
-
ClientId 客户端名称: 所有 client 都需要一个名称,broker 会根据 client 名称来跟踪会话,因此 client 名称必须是 唯一的。如果连接到 broker 时已经有一个重名的 clientId,那么会先断开现有 client 的连接,这将可能导致断开和连接的死循环,因为大多数 MQTT client 有断线重连机制;
-
CleanSession 持久会话: 当 client 连接到 broker 时,可以使用持久连接或非持久连接,
CleanSession
标志决定是否使用持久连接(当CleanSession = 0
时表示持久连接),对于持久会话,broker 会存储会话状态;而对于非持久会话,broker 不会存储 client 的任何内容,具体见第 4.2 节 · 会话状态; -
UserName & Password 用户名 & 密码: 用于 broker 认证和授权;
-
KeepAlive 保活探测间隔: KeepAlive 是以秒单位的时间间隔,指 client 发送两次消息的最大时间间隔,当 client 和 borker 之间在一段时间内没有数据交互时,client 会发送 PINGREQ 探测消息 用于判断连接是否正常,来决定是否要关闭该连接。KeepAlive 是 MQTT 协议的保活机制,从作用上看与 TCP 的 Keepalive 保活机制是非常类似的,不过 MQTT 协议的保活机制是应用层 client 实现的,而 TCP 的保活机制是 “内核” 实现的。
-
Last Will Message 遗嘱消息: 遗嘱消息用于通知意外停机的 client,每个 client 在连接时可以设置一个遗嘱消息,这个遗嘱消息会存储在 broker 上。当 client 因 “非正常原因” 断开连接时,broker 会将遗嘱消息分发给订阅了
“Will”
主题的 client。另外,这条遗嘱消息还可以设置是否为保留消息(Will Retain
标志)以及服务质量等级(Will Qos
)。
CONNACK 连接确认:
CONNACK
消息用于确认 CONNECT
消息。CONNECT
是 client 发送给 broker 的首个消息,相应地,broker 发送给 client 的首个消息一定是 CONNACK
消息。在 CONNACK
消息中,主要包含以下内容:
-
SessionPresent 持久会话:
SessionPresent
标志表示当前 broker 是否持有与 client 的持久会话。当 broker 接收了一个非持久会话连接(CleanSession = 1
),SessionPresent 的值始终为 0;而当 broker 接收了一个持久会话连接(CleanSession = 0
),则 SessionPresent 的值取决于 broker 是否存储了 ClientId 的会话状态; -
ReturnCode 响应码: 用于表示连接请求是否成功,如果响应码不为 0,则表示连接失败。
具体取值如下表:
返回码 | 描述 |
---|---|
0 | 连接已接受 |
1 | 连接被拒绝,不可接受的协议版本 |
2 | 连接被拒绝,标识符被拒绝 |
3 | 连接被拒绝,服务器不可用 |
4 | 连接被拒绝,用户名或密码错误 |
5 | 连接被拒绝,未授权 |
DISCONNECT 断开连接:
DISCONNECT
消息由 client 发送给 broker,用于断开连接。DISCONNECT 消息没有可变报头和有效载荷,也没有对应的确认应答消息,表示一个干净利索地断开连接操作。断开连接后,client 不能再发送除 CONNECT
消息之外的消息,broker 也需要丢弃和当前会话的遗嘱消息。
3.2 订阅消息
MQTT 是基于发布订阅模型的协议,在建立连接后,client 可以向 broker 订阅感兴趣的一个或多个话题。
3.2.1 SUBSCRIBE 订阅
SUBSCRIBE
消息由 client 发送给 broker,用于订阅感兴趣的话题,SUBSCRIBE
消息主要包含以下内容:
-
主题过滤器列表:
SUBSCRIBE
消息的有效载荷中至少需要包含一个话题过滤器,每个过滤器由一个 Topic 和 QoS 组成,其中的 QoS 指定了指定 client 接受的最大 OoS 等级。
3.2.2 SUBACK 订阅确认
SUBACK
消息用于确认 SUBSCRIBE
消息。SUBACK
消息主要包含以下内容:
- 返回码列表: 每个返回码都与 SUBSCRIBE 消息中的话题过滤器一一对应。
具体取值如下表:
返回码 | 描述 |
---|---|
0x00 | 订阅成功,最大 QoS 为 0 |
0x01 | 订阅成功,最大 QoS 为 1 |
0x02 | 订阅成功,最大 QoS 为 2 |
0x80 | 订阅失败 |
3.2.3 UNSUBSCRIBE 退订
UNSUBSCRIBE
消息由 client 发送给 broker,用于退订不感兴趣的话题,UNSUBSCRIBE
消息主要包含以下内容:
- 话题列表: UNSUBSCRIBE 消息的有效载荷中至少需要包含一个话题。
3.2.4 UNSUBACK 退订确认
UNSUBACK
消息用于确认 UNSUBSCRIBE
消息。UNSUBACK
消息非常简单,只有一个包唯一标识(位于可变报头)。
3.3 发布消息
当 MQTT client 在连接到 broker 之后就可以发送消息了,每条 PUBLISH
消息都包含一个 topic ,broker 会根据 topic 将消息发送给感兴趣的 client。除此之外,每条消息还会包含一个 Payload,Payload 是真正发布的应用消息,载荷的内容和格式由应用层决定,MQTT 协议层不关心。
3.3.1 PUBLISH 发布
PUBLISH
消息可以由 client 发送给 broker,也可以由 broker 发送给 client,用来运送应用层消息。PUBLISH 消息主要包含以下内容:
-
QoS 发布服务质量标志: 标记当前
PUBLISH
消息传送的交付保证水平,分为三个等级,具体见第 4.3 节 · 发布服务质量:- QoS 0(默认): 最多发一次
- QoS 1: 最少发一次
- QoS 2: 正好发一次
-
RETAIN 保留消息标志: 标记当前
PUBLISH
消息是否为保留消息,当 client 发送给 broker 的 PUBLISH 消息标记 RETAIN = 1 时,broker 会存储该消息,当新的 client 注册订阅时,并且匹配该消息主题时,该保留消息会发送给订阅者,具体见第 4.4 节 · 保留消息; -
DUP 重传标志: 标记当前的
PUBLISH
/PUBREL
消息是否为重复发送消息。 MQTT 协议规定了两种消息重传的场景,具体见第 4.5 节 · 消息重传; -
TopicName 话题名: 表示载荷数据的发布通道;
-
包唯一标识: 只有 QoS1 和 OoS2 的
PUBLISH
消息中存在; -
载荷(应用消息):
PUBLISH
消息的载荷是真正发布的应用消息,载荷的内容和格式由应用层决定,MQTT 协议层不关心。另外,载荷的数据长度等于:固定报头中的剩余长度(Remaining Lenght)- 可变报头的长度,载荷长度也可以为零。
3.3.2 发布确认
PUBLISH 消息的接收方需要发送确认应答,不同 QoS 等级的 PUBLISH 消息响应的消息不同:
发布服务质量等级 QoS | 期望的确认应答 |
---|---|
QoS 0 | 无确认应答 |
OoS 1 | PUBACK 消息 |
OoS 2 | PUBREC 消息 PUBREL 消息 PUBCOMP 消息 |
3.4 Ping 心跳探测
当 client 和 broker 在一段时间内没有数据交互时,client 会发送 PINGREQ
探测消息,用于判断连接是否正常,来决定是否要关闭该连接,这就是 MQTT 协议的保活机制。
3.4.1 PINGREQ 探测消息
PINGREQ 消息由 client 发送给 broker。
3.4.2 PINGRESP 探测确认
PINGRESP 消息由 broker 发送给 client,代表 client 是存活的。
4. MQTT 协议核心特性
4.1 主题和主题过滤器
MQTT 主题本质上是一种 “寻址形式”,用于将应用层消息分发到期望的客户端。MQTT 主题是一种类似于文件系统的分层结构,使用 “/” 正斜杠 作为分隔符。
4.1.1 主题格式规范
- 1、区分大小写;
- 2、采用 UTF-8 编码的字符串;
- 3、非空字符串,至少包含一个字符才有效;
- 4、可以包含空;
- 5、一个主题增加 “/” 前缀或后缀后是不同主题。
4.1.2 主题通配符
客户端订阅主题时,可以订阅确定的主题(例如 “group/group123”),也可以使用 “通配符” 来同时订阅多个主题。需要注意的是:在发布消息时不允许使用主题通配符,client 每次发布消息只能发布到单个主题。
-
单级通配符:
+
是单级通配符,单级通配符可以用于任何一个主题级别,但只能匹配一个级别。例如:
主题 | 匹配主题举例 |
---|---|
group/+/123 | group/vip/123 group/temp/123 |
-
多级通配符:
#
是多级通配符,多级通配符可以匹配多个连续级别。需要注意,多级通配符只能用于主题的最后一个级别。例如:
主题 | 匹配主题举例 |
---|---|
group/# | group group/123 group/vip/123 group/temp/123 |
4.1.3 $SYS 主题
$SYS
主题是 broker 上默认创建的只读主题,除此之外,broker 不会默认创建任何主题,所有主题都是由客户端订阅或发布才创建的,都不是永久性的。关于 $SYS
主题的更多介绍在 这里
4.1.4 主题的生存周期
- 创建主题:某个客户端订阅该主题,或者某个客户端向主题发布消息,同时设置为保留消息;
- 删除主题:订阅该主题的最后一个客户端断开连接,同时连接为非持久会话(CleanSession = 1)。
4.2 会话状态
当 client 连接到 broker 时,可以使用持久连接或非持久连接,这是通过 CONNECT
消息中的 CleanSession 标志来决定的(当 CleanSession = 0
时表示持久连接)。对于持久会话,broker 会存储会话状态;而对于非持久会话,broker 不会存储 client 的任何内容。会话状态主要包含以下内容:
4.2.1 客户端存储的会话状态
- 已经发送 broker 但没有收到确认的 QoS 1 和 QoS 2
PUBLISH
消息; - 从 broker 接收但还没有收到确认的 QoS 2
PUBLISH
消息。
4.2.2 服务端存储的会话状态
- 客户端的订阅;
- 已经发送到 client 的但没有得到确认的 QoS 1 和 QoS 2
PUBLISH
消息; - 从客户端接收但还没有确认的 QoS 2
PUBLISH
消息; - 等待发送到 client 的 QoS 1 和 QoS 2
PUBLISH
消息; - (可选项)等待发送到客户端的 QoS 0
PUBLISH
消息。
提示: 保留消息不属于会话状态,在会话结束时不会被删除,broker 应该一直存储保留消息直到被 client 删除。
4.3 QoS 发布服务质量等级
- QoS 0(默认): 最多发一次(不保证消息交付)
- QoS 1: 最少发一次(保证消息交付,但可能出现重复)
- QoS 2: 正好发一次(保证没有重复的消息交付)
QoS 0 等级的 PUBLISH
消息的交付能力完全依赖于底层传输层,QoS 1 和 QoS 2 等级开始在应用层提高 PUBLISH
消息的交付能力。当消息丢失时,发送端会重新发送早前尝试发送过的 PUBLISH
消息(DUP = 1),接收者收到消息也会发送确认响应消息。
4.3.1 QoS 0 · 最多发一次
在 QoS 0 的等级的 PUBLISH
消息中不包含包唯一标识。发送者不考虑消息交付结果,接收者也不发送响应。接收者最多只能收到一次消息,也有可能一次也收不到。
4.3.2 OoS 1 · 最少发一次
在 QoS 1 等级的 PUBLISH
消息中包含包唯一标识,发送方会一直将该消息当作 “未确认” 的消息,直到收到对应的 PUBACK
确认消息。具体消息流如下:
提示: 实际的消息传递是在 client 和 broker 之间进行的,这 4 个步骤是简化为发送方和接收方之间的消息传递。
- 1、发送方存储应用消息;
- 2、发送方发送
PUBLISH
(QoS = 1, DUP = 0,)消息; - 3、接收方收到
PUBLISH
消息,并响应PUBACK
()确认消息; - 4、发送方收到
PUBACK
消息,并删除存储的应用消息。
4.3.3 QoS2 · 正好发一次
QoS 2 是最高的服务质量,保证消息不会丢失也不会重复,缺点是会增加开销。在 QoS 2 等级的 PUBLISH
消息中包含包唯一标识,发送者会一直将该消息当作 “未确认” 的消息,知道收到对应的 PUBCOMP
确认消息。
- 1、发送方存储消息;
- 2、发送方发送
PUBLISH
(QoS = 2, DUP = 0,)消息; - 3、接收方收到
PUBLISH
消息,并存储消息; - 4、接收方响应
PUBREC
()消息; - 5、发送方收到
PUBREC
消息,并发送PUBREL
()消息; - 6、接收方向上层应用通知消息;
- 7、接收方响应
PUBCOMP
()消息; - 8、发送方收到
PUBCOMP
消息,并删除存储的应用消息。
4.4 RETAIN 保留消息
当 client 发布某个主题的消息时,broker 会将该消息分发给任何已订阅该主题的 client,随后这条消息会从 broker 上删除。可以设置 RETAIN
保留标志设置该 PUBLISH 消息为保留消息,broker 会存储该主题的最后一条保留消息,当新的 client 注册订阅时,并且匹配该消息主题时,该保留消息会发送给订阅者。需要注意:broker 只会为每个主题保存最近一条保留消息,新收到的 RETAIN = 1 的消息会覆盖原本那条保留消息;
持久会话 & 服务质量等级 & 保留消息都会影响新订阅者是否接受消息,总结如下表:
- 对于保留消息(Retain Flag 为 Ture),新订阅者总能收到最后一条保留消息(图中绿色部分);
- 对于持久会话(Clean Session Flag 为 Flase)且订阅者订阅 OoS 大于等于 1,总能收到所有 OoS 大于等于 1 的消息(图中黄色部分)。
4.5 消息重传
标记 DUP = 1
的消息是被重复发送的消息,MQTT 消息重传有 2 种场景:
- 1、PUBLISH / PUBREL 消息发送后,在规定时间内没有收到确认应答消息,则重传这个消息;
- 2、在使用持久会话时,client 重新连接后,broker 会自动重传未确认的消息。
需要注意:DUP 标志只对 OoS > 0 的消息有效,所有 QoS = 0 的消息 DUP 标志必须设置为 0;
TCP 协议有报文重传机制,为什么 MQTT 协议还有消息重传机制?
TCP 协议的报文重传机制是对所有 TCP 报文有效的重传机制,而 MQTT 协议的消息重传机制只对一小部分消息有效,用于实现更可靠的消息交付保证。虽然 TCP 协议在一般情况下可以保证不丢包,但是这并不是绝对的,依然存在请求超时或者连接中断等情况。而 MQTT 协议的 QoS 1 和 QoS 2 要求更可靠的交付能力,并且需要在客户端重连后也能保证交付。因此,MQTT 协议也定义了一个消息重传机制。
5. 总结
到这里,关于 MQTT 协议的工作原理 & 协议消息格式 & 核心特性等内容就介绍完了。我知道你应该会对 MQTT 协议的实战应用更加感兴趣,下一篇文章里,我将带你实现基于 MQTT 协议的 IM 服务,请关注。
参考资料
- MQTT 官网
- MQTT 协议中文版
- MQTT Protocol Guide —— Steve 著
- MQTT 协议是个啥?这篇文章告诉你! —— cxuan 著
- Android消息推送 MQTT 实战 —— wildma 著
- Battle of The Protocols (HTTP vs. Websockets vs. MQTT) —— Ronak Singh 著
我是小彭,带你构建 Android 知识体系。技术和职场问题,请关注公众号 [彭旭锐]私信我提问。