为什么最近每份 Android 简历都说 “熟悉 MQTT 协议”?
请点赞关注,你的支持对我意义重大。
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 知识体系。技术和职场问题,请关注公众号 [彭旭锐]私信我提问。
为什么最近每份 Android 简历都说 “熟悉 MQTT 协议”?的更多相关文章
- 优秀Android开发简历都是这么写,你学会也可以进大厂
最近收了很多程序员的简历,工作经验从1年到十几年不等.发现一个问题,工作经验范围差不多的程序员,简历看起来也差不多... 为啥程序员的简历如此统一?正好最近看到一个分享也分析了这个问题,结合我个人的一 ...
- Android 简历+面试题 汇总
1.教你写简历 1.1.你真的会写简历吗? 1.2.80%以上简历都是不合格的 1.3.推荐两个技术简历模板 1.4.关于程序员求职简历 1.5.程序员简历模板列表 2.面试题 2.1.国内一线互联网 ...
- 不会看 Explain执行计划,劝你简历别写熟悉 SQL优化
昨天中午在食堂,和部门的技术大牛们坐在一桌吃饭,作为一个卑微技术渣仔默默的吃着饭,听大佬们高谈阔论,研究各种高端技术,我TM也想说话可实在插不上嘴. 聊着聊着突然说到他上午面试了一个工作6年的程序员, ...
- 解决Android Studio启动速度慢的问题。避免每次启动Android Studio都要fetching Android sdk compoment information。
Android Studio每次启动都要去fetching sdk,由于Android sdk 官网在大陆连不上,所以每次启动时界面都会停在那里很久. 解决办法就是设置取消每次fetching sdk ...
- Android消息推送(二)--基于MQTT协议实现的推送功能
国内的Android设备,不能稳定的使用Google GCM(Google Cloud Messageing)消息推送服务. 1. 国内的Android设备,基本上从操作系统底层开始就去掉了Googl ...
- 采用MQTT协议实现android消息推送(1)MQTT 协议简介
1.资料 mqtt官网 http://mqtt.org/ 服务端程序列表 https://github.com/mqtt/mqtt.github.io/wiki/servers 客户端库列表 http ...
- Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)
本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...
- MQTT协议学习及实践(Linux服务端,Android客户端的例子)
前言 MQTT(Message Queuing Telemetry Transport),是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提 ...
- MQTT协议实现Android中的消息收发
前言 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输),基于发布/订阅范式的消息协议,是一种极其简单和轻量级的消息协议,专为受限设备和低带宽.高延迟 ...
随机推荐
- 使用Playbook批量部署多台LAMP环境
1. 安装ansible yum install epel-release -y yum install ansible -y Playbook是一个不同于使用ansible命令行执行方式的模式,功能 ...
- 抓包整理外篇fiddler————了解工具栏[一]
前言 抓包本篇还没写完,因为在工作中,发现有人用fiddler 用的还不是很好,所以去介绍一下这个东西,fiddler大体分为10多个章节. 正文 首先了解一下fiddler的抓包原理哈. 可以看到当 ...
- 『现学现忘』Docker基础 — 42、补充:save和load命令说明
目录 1.save命令 2.load命令 1.save命令 将指定的一个或多个镜像保存成.tar格式的文件,进行打包归档. 查看docker save帮助命令,如下: [root@192 ~]# do ...
- Consider defining a bean of type 'redis.clients.jedis.JedisPool' in your configuration.
报错信息 原因是没有Jedispool没有注入 import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml ...
- idea控制台不能输入问题
idea控制台不能输入问题 在idea中,使用JUnit测试时,不能在控制台输入,进行下面的设置即可 第一步 :help---> Edit Custom VM Options... 第二步:添加 ...
- 159_模型_Power BI 地理分析之形状地图
159_模型_Power BI 地理分析之形状地图 声明以下地图元素仅供学习交流所用,如需地图公开使用请提前做好报审工作. 一.背景 当企业的体量达到一定体量的时候,保持稳定的增长是非常重要的事情.本 ...
- skip-host-cache skip-name-resolve
在mysql 的data 文件夹下 生成了一个.err的文件,打开发展,经常有人访问这个,服务器部署在腾讯云上. 2017-05-23 0:49:04 2996 [Warning] IP addres ...
- CSS面试总结
文章首次发表:_时雨_CSDN 1. BFC:块级格式化上下文(重点关注) BFC基本概念:BFC是 CSS布局的一个概念,是一块独立的渲染区域(环境),里面的元素不会影响到外部的元素. BFC原理( ...
- 算法竞赛进阶指南 0x43 线段树
目录 线段树简介 线段树的简单代码实现 建树代码 修改操作 查询操作 线段树的查询操作的时间复杂度分析: AcWing245. 你能回答这些问题吗 思路 代码[时间复杂度:\(O( \space(N+ ...
- el-form 每行显示两列,底部按钮居中
需求: el-form 每行显示两列,底部按钮居中 问题: 以前的解决办法是: el-col, el-row.但是这里只有一个 el-form-item 的 label 数据是已知的,其余项都是循环得 ...