index
约 3512 字大约 12 分钟
2026-05-02
控制报文
MQTT 目前定义了 15 种控制报文类型,按照功能可以将这些报文分为连接、发布、订阅三类:
| 类别 | 名称 | 说明 |
|---|---|---|
| 链接 | CONNECT | 用于客户端向服务端发起连接 |
| CONNACK | 作为响应返回连接的结果 | |
| DISCONNECT | 想要结束通信,或者遇到了一个必须终止连接的错误,发送该报文关闭网络连接 | |
| AUTH | MQTT 5.0 引入的全新的报文类型,仅用于增强认证,为客户端和服务端提供更安全的身份验证。 | |
| PINGREQ | 用于连接保活和探活,客户端定期发出 PINGREQ 报文向服务端表示自己仍然活跃,然后根据 PINGRESP 报文是否及时返回判断服务端是否活跃 | |
| PINGRESP | 用于连接保活和探活,客户端定期发出 PINGREQ 报文向服务端表示自己仍然活跃,然后根据 PINGRESP 报文是否及时返回判断服务端是否活跃 | |
| 发布 | PUBLISH | 用于发布消息 |
| PUBACK | 分别用于 QoS 1 和 QoS 2 消息的确认流程 | |
| PUBREC | 分别用于 QoS 1 和 QoS 2 消息的确认流程 | |
| PUBREL | 分别用于 QoS 1 和 QoS 2 消息的确认流程 | |
| PUBCOMP | 分别用于 QoS 1 和 QoS 2 消息的确认流程 | |
| 订阅 | SUBSCRIBE | 用于客户端向服务端发起订阅 |
| UNSUBSCRIBE | 用于客户端向服务端发起取消订阅 | |
| SUBACK | 返回订阅结果 | |
| UNSUBACK | 返回取消订阅结果 |
报文格式
MQTT 的报文格式分为三部分:固定报头、可变报头、有效载荷 固定报头固定存在于所有控制报文中,而可变报头和有效载荷是否存在以及它们的内容则取决于具体的报文类型。
固定报头
固定报头还包含一下三部分
- 报文类型:控制报文的 15 个类型
- 标识位:到 MQTT 5 为止只有
PUBLISH报文明确规定了含义,其他报文仍是保留- Bit 3:DUP,表示当前 PUBLISH 报文是否是一个重传的报文
- Bit 2,1:QoS,表示当前 PUBLISH 报文使用的服务围量等级
- Bit 0:Retain,表示当的 PUBLISH 报文是否是一个保留消息
- 剩余长度:控制报文剩余的字节数=可变报头+有效载荷
可变报头
通常包含一下以下五部分,五部分并不是必须,不同报文类型的可变报头是不一样的:
- 协议名称
- 协议级别
- 连接标识
- 保活字段
- 常见属性
有效载荷
有效载荷是用于实现对应报文的核心功能:
- 在 PUBLISH 报文中,PayLoad 用于承载具体的应用消息内容,这也是 PUBLISH 报文最核心的功能。
- 在 SUBSCRIBE 报文中,PayLoad 包含了想要订阅的主题以及对应的订阅选项,这也是 SUBSCRIBE 报文最主要的工作。
主题
MQTT 主题本质上是一个 UTF-8 编码的字符串,是 MQTT 协议进行消息路由的基础。MQTT 主题类似 URL 路径,使用斜杠/进行分层
MQTT 主题不需要提前创建。MQTT 客户端在订阅或发布时即自动的创建了主题,开发者无需再关心主题的创建,并且也不需要手动删除主题。
主题通配符
MQTT 主题通配符包含单层通配符+及多层通配符#,主要用于客户端一次订阅多个主题。
单层通配符
加号是用于单个主题层级匹配的通配符。在使用单层通配符时,单层通配符必须占据整个层级,例如:
+ 有效
test/+ 有效,表示test下所有的主题
test/+/temperature 有效,表示test下包含temperature子主题的主题,但不包含双层以及0层(如test/1/2/temperature或test/temperature)
test+ 无效多层通配符
井号用于多个主题层级匹配的通配符
test/+/temperature 有效,表示test下包含temperature子主题的主题,可以包含双层以及0层(如test/1/2/temperature或test/temperature)系统主题
mqtt 并没有规定系统主题的写法,但大多数开发者都遵循以 $SYS/ 开头的主题为系统主题 系统主题主要用于获取MQTT服务器自身运行状态、消息统计、客户端上下线事件等数据。目前,MQTT协议暂未明确规定$ SYS/主题标准,但大多数 MQTT 服务器都遵循该标准建议。
会话
MQTT 客户端和 MQTT 服务器之间的连接被称为会话。每个 MQTT 客户端都可以启动一个或多个会话,通过会话可以实现客户端和服务器之间的消息传递。
常见的配置参数 Clean Start CleanStart 作用:用于指示客户端在和服务器建立连接的时候应该尝试恢复之前的会话还是直接创建全新的会话。常见取值以及含义: 0:服务端存在一个关联此客户端标识符(CLientID)的会话,服务端必须基于此会话的状态恢复与客户端的通信(之前的订阅信息会再次绑定,并且会接收到客户端断开时,发布者所发布的消息)。如果不存在任何关联此客户端标识符的会话,服务端必须创建一个新的会话。 1:客户端和服务端必须丢弃任何已存在的会话,并开始一个新的会话。
Session Expriy Interval 决定了会话状态数据在服务端的存储时长。常见取值:
- 没有指定此属性或者设置为日,表示会话将在网络连接断开时立即结束。
- 设置为一个大于日 的值,则表示会话将在网络连接断开的多少秒之后过期。
- 设置为θxFFFFFFFF,即 SessionExpiryInterval 属性能够设置的最大值时,表示会话数据永不过期。 服务端使用 ClientID 来唯一地标识每个会话,如果客户端想要在连接时复用之前的会话,那么必须使用与此前一致的 ClientID。
消息
- 普通消息:普通消息在发送之前其所对应的主题如果不存在订阅者,普通消息 MQTT 服务器会直接将其丢弃。
- 保留消息:保留消息可以保留在 MQTT 服务器中。任何新的订阅者订阅与该保留消息中的主题匹配的主题时,都会即接收到该消息,即使这个消息是在它们订阅主题之前发布的。
- MQTT 服务器会为每个主题存储最新一条保留消息
- 在保留消息发布前订阅主题,将不会收到保留消息。需要待保留消息发布后,重新订阅该主题(重新连接也可以),才会收到保留消息。
- 遗嘱消息:当该客户端意外断开连接,服务端就会向其他订阅了相应主题的客户端发送此遗嘱消息。
- 遗嘱消息在客户端发起连接时指定,它和 ClientID、CLeanStart 这些字段一起包含在客户端发送的 CoNNECT 报文中。
- 遗嘱消息有一个专属属性 Will Delay Interval 表示服务端在网络连接关闭后延迟多久发布遗嘱消息,单位是秒
- 即使没有指定遗嘱消息,服务端也会默认发送一个遗嘱消息,遗嘱消息是属于会话的一部分,当会话结束时遗嘱消息也无法生效
[!NOTE] Title 遗嘱消息是会话状态的一部分,当会话结束,遗嘱消息也无法继续单独存在。但是在遗嘱消息延迟发布期间,会话可能过期,也可能因为客户端在新的连接中设置 CleanStart 为 1 所以服务端需要丢弃之前的会话。为了避免丢失遗嘱,此时服务端必须发布该遗嘱消息,即便 WitlDelayInterval 还没有到期。所以服务端最终何时发布遗嘱消息,取决于 WillDelayInterval 到期会话结束这两种情况谁先发生。
延迟发布
MQTT 服务端收到发布者发布的消息以后,延迟一段时间以后再把消息转发给订阅者
延迟发布主题格式:$delayed/{DelayInterval}/{TopicName} 单位为秒
用户属性
HQTT 5.0 版本引入的一个新特性。它允许在 Publish,Subscribe、Connect、Disconnect 等报文中携带附加信息。 类似于 http 协议的请求头
应用场景 1、日志记录:在发布(PUBLISH)和订阅(SUBSCRIBE)报文中加入用户属性,可以帮助记录操作者信息、操作时间、原因说明等,便于后续的审计跟踪和问题排查。 2、消息分类与标记:用户雇性可以用来给消息添加标签或分类信息,如消息类型等,使得接收方能根掘这些量性对消息进行过滤、排序或特殊处理。
订阅
订阅选项
QOS
mqtt 是基于 tcp 协议的,但 tcp 协议只在有网的情况下保证可靠传输,如果网络中断那么后面的数据依旧无法传输
QOS:消息的质量等级 QOS 常见的取值:
- 0:消息最多发送一次。可能会导致消息的丢失。消息即发即弃,不需要等待确认,不需要存储和重传,因此对于接收方来说,永远都不需要担心收到重复的消息。
- 1:消息至少发送一次。肯定不会出现消息的丢失,消息可能会重复。引用了应答和重传机制
- 2:消息仅有一次发送。消息肯定不会丢失,消息也不会重复 在发送消息的时候可以指定消息的质量等级,一股情况下 broker 获取到这个消息以后,就会按照发送时所指定的消息的质量等级把消息发给订阅者。特殊情况下(当订阅者指定了订阅时消息的最大的质量等级),消息质量等级可能会发生改变。
客户端在订阅时会向服务端发送一条消息,其中包含自己需要请求的最大 qos 服务端支持的 qos 会有不同的情况,对此需要额外的处理
- 服务端支持的最大 qos<客户端订阅时请求的最大 qos,服务端会返回消息并且携带自己所支持的最大 qos,客户端接收到消息后再判断自己是否要订阅或是做其他处理
- 请阅示请求的最大 qos<消息发布时的 qos,为了尽可能的投递消息,服务端不会忽略这些消息,而是在转发消息时做降级处理
No Local
服务端是否将消息转发给发布这个消息的客户端
- 0:默认值,可以转发
- 1:不可以转发
通常用于桥接的场景下:两个 MQTT Server 简历的 MQTT 连接,然后相互订阅一些主题 在桥接场景中如果没有适当的将 No Local 设置为 0 会导致转发风暴
Retain As Published
服务端在转发保留消息时是否清除 Retain 字段(保留消息通过 Retain 字段区分)
- 0:默认值,不清除
- 1:清除
Retain Handling
当建立连接时是否发送保留消息
- 0:默认值,只要建立连接就发送保留消息
- 1:只有简历全新连接(不是重复订阅)时才发送
- 2:建立连接时不发送
共享订阅
在普通的订阅中,服务端会把一个消息的副本发送给所有的订阅者,如果生产者生产消息的速度大于消费者消费消息的速度时,就会导致阻塞,只有所有订阅者都消费完同样的消息后,服务端才会再发送下一条消息。 在共享订阅中服务端会把消息均衡的发给客户端,一条消息只能有一个订阅者消费,只要有一个订阅者消费了这条消息那么这个消息就已经完成
分类
| 前缀格式 | 示例 | 前缀 | 说明 |
|---|---|---|---|
| 带群组的格式 | $shared/abc/t/1 | $shared/abc | abc 表示订阅群组名,t/1 才是真实的主题 |
| 不带群组的格式 | $queue/t/1 | $queue | t/1 是真实的主题 |
负载均衡
- 随机(Random),在共享订阅组内随机选择一个会话发送消息。
- 轮询(RoundRobin),在共享订阅组内按顺序选择一个会话发送消息,循环往复。
- 哈希(Hash),基于某个字段的哈希结果来分配。
- 粘性(Sticky),在共享订阅组内随机选择一个会话发送消息,此后保持这一选择,直到该会话结果再重复这一过程
- 本地优先(Local),随机选择,但优先选择与消息的发布者处于同一节点的会话,如果不存在这样的会话,则退化为普通的随机策略。
排他订阅
排它订阅允许对主题进行互斥订阅,一个主题同一时刻仅被允许存在一个订阅者,在当前订阅者未取消订阅前,其他订阅者都将无法订阅对应主题。
| 示例 | 前缀 | 主题名 |
|---|---|---|
| $exclusive/t/1 | $exclusive/ | t/1 |
常见错误码:
- 0x8F:使用了$exclusive/,但未开启排他订阅
- 0x97:已经有客户端订阅了该主题
自动订阅
自动订阅能够给 EMQX 设置多个规则,在设备成功连接后按照规则为其订阅指定主题,不需要额外发起订阅。
贡献者
版权所有
版权归属:wynnsimon
