想要深入理解某一种网络协议,那么对其报文结构肯定要了如指掌,因为这是其设计的基础,一切功能都是和报文息息相关的。这篇文章就来剖析一下 mqtt 协议的报文结构,这样在日后开发相关通信库的时候,才能够得心应手。
mqtt 控制报文结构
mqtt 协议通过交换预定义的 mqtt 控制报文来进行通信,该控制报文由三部分组成:
- 固定报头 :mqtt 所有报文均包含该部分
- 可变报头 :仍然属于报文控制头部,只是并不是所有报文都需要这部分控制信息
- 有效载荷 :可以理解为数据部分,有些类型的报文不需要
固定报头
报文类型
如上图所示,固定报头至少占两个字节,最多占五个字节,其中第一个字节高四位用于指定控制报文的类型。mqtt 协议共有 14 种控制报文,所以,固定报头高四位值为 0
和 15
的报文为保留报文
名称 | 值 | 方向 | 描述 |
---|---|---|---|
Reserved | 0 | 保留 | |
Reserved | 15 | 保留 |
建立、确认与释放连接
连接,连接确认与释放连接的控制报文类型如下:
名称 | 值 | 方向 | 描述 |
---|---|---|---|
CONNECT | 1 | 客户端——>服务端 | 客户端请求连接服务端 |
CONNACK | 2 | 服务端——>客户端 | 连接报文确认 |
DISCONNECT | 14 | 客户端——>服务端 | 客户端断开与服务端的连接 |
数据传输相关
mqtt 协议种使用发布 来描述数据传输的过程,包括 :
- 客户端主动向服务器端发布一个主题
- 服务器端向客户端发布其订阅的主题消息
这两种通信过程都是使用 PUBLISH
相关报文来进行消息传递的,基于不同的 QoS ,PUBLISH
包含多种行为方式
名称 | 值 | 方向 | 描述 |
---|---|---|---|
PUBLISH | 3 | 两个方向都允许 | 消息发布 |
PUBACK | 4 | 两个方向都允许 | QoS1 消息发布收到确认 |
PUBREC | 5 | 两个方向都允许 | QoS2 消息发布收到(保证交付第一步) |
PUBREL | 6 | 两个方向都允许 | QoS2 消息发布释放(保证交付第二步) |
PUBCOM | 7 | 两个方向都允许 | QoS2 消息发布完成(保证交付第三步) |
订阅与取消订阅
客户端可以使用订阅和取消订阅报文向服务器表明自己感兴趣的主题,该报文类型如下:
名称 | 值 | 方向 | 描述 |
---|---|---|---|
SUBSCRIBE | 8 | 客户端——>服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端——>客户端 | 服务器端订阅报文确认 |
UNSUBSCRIBE | 10 | 客户端——>服务端 | 客户端取消订阅请求 |
UNSUBACK | 11 | 服务端——>客户端 | 服务器端取消订阅报文确认 |
心跳包
客户端发送 心跳 (Keep Alive)PINGREQ
报文给服务端的。用于:
- 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
- 请求服务端发送响应确认服务端还活着。
- 使用网络以确认网络连接没有断开。
服务端发送 心跳响应 PINGRESP
报文响应客户端的 PINGREQ
报文,表示服务端还活着。
名称 | 值 | 方向 | 描述 |
---|---|---|---|
PINGREQ | 12 | 客户端——>服务端 | 客户端心跳请求 |
PINGRESP | 13 | 服务端——>客户端 | 服务器端心跳响应 |
标志位
第一个字节低四位 [3:0],用来包含每个 MQTT 控制报文类型特定的标志
可以看到,大部分类型的报文都不使用标志位,对于 PUBLISH
报文,这些标志位的作用在下文会详细介绍
剩余长度
剩余长度字段描述了 可变报头+有效载荷 的长度,使用变长编码方案,最大支持四个字节,具体规则如下:
每个字节的低 7 位用于编码数据,最高位是标志位,标志位标志着剩余长度字段是否已经结束。举个例子长度为16383 字节的,如下编码:
第一字节:1111 1111
==> 标志位为 1
,代表该字段未结束,此字节有效数据 111 1111
第二字节:0111 1111
==> 标志位为 0
,代表该字段已结束,此字节有效数据 111 1111
最终有效数据 :111 1111 111 1111
即 0x3FFF
(16383)
可变报头
报文标识符
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同可变报头的报文标识符(数据包标识符)字段存在于在多个类型的报文里。
对于是否使用以及如何使用报文标识符,要遵循以下规则:
PUBLISH
发布相关的报文:- QoS0 时,
PUBLISH
不能使用报文标识符 - QoS > 0 时,
PUBLISH
必须包含一个非零的 16 位报文标识符 - QoS1 时,
PUBACK
报文必须包含与最初发送的PUBLISH
报文相同的报文标识符 - QoS2 时,
PUBACK
、PUBREC
、PUBREL
、PUBCOMP
报文必须包含与最初发送的PUBLISH
报文相同的报文标识符
- QoS0 时,
SUBACK
和UNSUBACK
必须包含在对应的SUBSCRIBE
和UNSUBSCRIBE
报文中使用的报文标识符对客户端和服务器端来说(订阅与取消订阅只与客户的相关):
每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。
重发报文时,必须使用与上一次相同的标识符。
当处理完这个报文对应的确认(QoS1的
PUBLISH
对应确认的是PUBACK
,Q0S2的PUBLISH
对应确认的是PUBCOMP
,与SUBSCRIBE
或UNSUBSCRIBE
对应确认的分别是SUBACK
或UNSUBACK
)后,这个报文标识符就释放可重用
其他部分
除了报文标识符,可变报头还包括其他部分,不过剩余部分和各个控制报文息息相关,后面再详细介绍。
有效载荷
某些 mqtt 控制报文会在报文最后部分包含一个有效载荷,对于 PUBLISH
报文,其有效载荷就是应用消息。下面列出了需要有效载荷的控制报文类型。