相关文章
1.《MQTT协议分析总结(一)》
2.《MQTT协议分析总结(二)》
3.《【IoT】如何连接到百度IoT云》
4.《【FreeRTOS】基于STM32移植LWIP 2.1.2之MQTT》
1.前言
在《MQTT协议分析总结(一)》基础上,本篇文章主要是详细介绍MQTT的每个报文的使用。
2. CONNECT – 连接
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT 报文。
在一个网络连接上,客户端只能发送一次CONNECT 报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符
,Will 主题
,Will 消息
,用户名
和密码
。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
2.1 固定报头 Fixed header
CONNECT报文的固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (1) | Reserved 保留位 | ||||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 |
2.2 可变报头 Variable header
CONNECT报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和 保持连接(Keep Alive)。
- 协议名字节构成
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
协议名 | |||||||||
byte 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 长度 LSB (4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte 4 | ‘Q’ | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte 5 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte 6 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
协议名是表示协议名 MQTT 的UTF-8编码的字符串。MQTT规范的后续版本不会改变这个字符串的偏移和长度。
如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文。对于后一种情况,按照本规范,服务端不能继续处理CONNECT报文。
实际抓包如下:
- Protocol Level byte协议级别字节构成
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
协议级别 | |||||||||
byte 7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
客户端用8位的无符号值表示协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)
的CONNACK报文响应CONNECT报文,然后断开客户端的连接。
实际抓包如下:
- 连接标志 Connect Flags
连接标志字节包含一些用于指定 MQTT 连接行为的参数。它还指出有效载荷中的字段是否存在。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 7 | User Name Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | |
* | * | * | * | * | * | 0 |
实际抓包如下:
- Clean Session(清理会话)
它指定了会话状态的处理方式。如果清理会话(CleanSession)
标志被设置为0
,服务端 必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。如果清理会话(CleanSession)
标志被设置为1
,客户端和服务端 必须丢弃之前的任何会话并开始一个新的会话。- Will Flag(遗嘱标志)
遗嘱标志(Will Flag)
被设置为1
,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息 。
如果遗嘱标志
被设置为0
,连接标志中的Will QoS和Will Retain字段必须设置为0,并且有效载荷中不能包含Will Topic和Will Message字段。网络连接断开时,不能发送遗嘱消息 。- Will QoS(遗嘱QoS)
位置:连接标志的第4和第3位。这两位用于指定发布遗嘱消息时使用的服务质量等级。- Will Retain(遗嘱保留)
如果遗嘱消息被发布时需要保留,需要指定这一位的值。- User Name Flag(用户名标志)
如果用户名(User Name)标志被设置为1,有效载荷中必须包含用户名字段。- Password Flag(密码标志 )
如果密码(Password)标志被设置为1,有效载荷中必须包含密码字段。
- 保持连接 Keep Alive
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
byte 9 | 保持连接 Keep Alive MSB | |||||||
byte 10 | 保持连接 Keep Alive LSB |
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ
报文。
实际抓包如下:
2.3 有效载荷 Payload
CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。
实际抓包如下:
3. CONNACK – 确认连接请求
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。
3.1 固定报头 Fixed header
CONNACK 报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (2) | Reserved 保留位 | ||||||
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (2) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
剩余长度字段
表示可变报头的长度。对于CONNACK
报文这个值等于2。
实际抓包如下:
3.2 可变报头 Variable header
CONNACK报文可变报头:
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
连接确认标志 | Reserved 保留位 | SP1 | |||||||
byte 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | X | |
连接返回码 | |||||||||
byte 2 | X | X | X | X | X | X | X | X |
-
连接确认标志 Connect Acknowledge Flags
第1个字节是 连接确认标志,位7-1
是保留位且必须设置为0
。
第0 (SP)位
是当前会话(Session Present)标志。 -
当前会话 Session Present
如果服务端收到清理会话(CleanSession)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0。如果服务端收到一个CleanSession为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。如果服务端已经保存了会话状态,它必须将CONNACK报文中的当前会话标志设置为1。如果服务端没有已保存的会话状态,它必须将CONNACK报文中的当前会话设置为0。还需要将CONNACK报文中的返回码设置为0 。
当前会话标志使服务端和客户端在是否有已存储的会话状态上保持一致。
-
连接返回码 Connect Return code
连接返回码字段使用一个字节的无符号值,在下面的表格中列出。如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码(表格中的某一个)的CONNACK报文。如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接。
值 | 返回码响应 | 描述 |
---|---|---|
0 | 0x00连接已接受 | 连接已被服务端接受 |
1 | 0x01连接已拒绝,不支持的协议版本 | 服务端不支持客户端请求的MQTT协议级别 |
2 | 0x02连接已拒绝,不合格的客户端标识符 | 客户端标识符是正确的UTF-8编码,但服务端不允许使用 |
3 | 0x03连接已拒绝,服务端不可用 | 网络连接已建立,但MQTT服务不可用 |
4 | 0x04连接已拒绝,无效的用户名或密码 | 用户名或密码的数据格式无效 |
5 | 0x05连接已拒绝,未授权 | 客户端未被授权连接到此服务器 |
6-255 | 保留 |
3.3 有效载荷 Payload
CONNACK报文没有有效载荷。
4. PUBLISH – 发布消息
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
4.1 固定报头 Fixed header
PUBLISH报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (3) | DUP | QoS-H | QoS- | RETAIN | |||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2... | 剩余长度 |
实际抓包如下:
- 重发标志 DUP
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。 - 服务质量等级 QoS
这个字段表示应用消息分发的服务质量等级保证。服务质量定义如下:
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分发一次 |
1 | 0 | 1 | 至少分发一次 |
2 | 1 | 0 | 只分发一次 |
- | 1 | 1 | 保留位 |
-
保留标志 RETAIN
如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志
被设置为1
,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。如果客户端发给服务端的PUBLISH报文的
保留(RETAIN)标志
被设置为0
,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。
4.2 可变报头 Variable header
可变报头按顺序包含主题名和报文标识符。
- 主题名 Topic Name
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。 - 报文标识符 Packet Identifier
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。
4.3 有效载荷 Payload
有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。
5. PUBACK –发布确认
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
5.1 固定报头 Fixed header
PUBACK报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (4) | 保留位 | ||||||
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
5.2 可变报头 Variable header
包含等待确认的PUBLISH报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
5.3 有效载荷 Payload
PUBACK报文没有有效载荷。
6. SUBSCRIBE - 订阅主题
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
6.1 固定报头 Fixed header
SUBSCRIBE报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (8) | 保留位 | ||||||
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 |
6.2 可变报头 Variable header
可变报头包含报文标识符。客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。
6.3 有效载荷 Payload
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文有效载荷中的主题过滤器列表必须是UTF-8字符串。
SUBSCRIBE报文有效载荷格式:
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
主题过滤器 | ||||||||
byte 1 | 长度 MSB | |||||||
byte 2 | 长度 LSB | |||||||
byte 3..N | 主题过滤器(Topic Filter) | |||||||
服务质量要求(Requested QoS) | ||||||||
保留位 | 服务质量等级 | |||||||
byte N+1 | 0 | 0 | 0 | 0 | 0 | 0 | X | X |
实际抓包如下:
7. SUBACK – 订阅确认
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
7.1 固定报头 Fixed header
SUBACK报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (9) | 保留位 | ||||||
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 |
7.2 可变报头 Variable header
可变报头包含等待确认的SUBSCRIBE报文的报文标识符。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | 报文标识符 MSB | |||||||
byte 2 | 报文标识符 LSB |
7.3 有效载荷 Payload
有效载荷包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
返回码 | ||||||||
byte 1 | X | 0 | 0 | 0 | 0 | 0 | X | X |
允许的返回码值:
- 0x00 - 最大QoS 0
- 0x01 - 成功 – 最大QoS 1
- 0x02 - 成功 – 最大 QoS 2
- 0x80 - Failure 失败
0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用。
8. DISCONNECT –断开连接
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
8.1 固定报头 Fixed header
DISCONNECT报文固定报头:
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (14) | 保留位 | ||||||
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | |
byte 2 | 剩余长度 (0) | |||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
8.2 可变报头 Variable header
DISCONNECT报文没有可变报头。
8.3 有效载荷 Payload
DISCONNECT报文没有有效载荷。
9.参考资料
MQTT 5.0协议中文版:
https://github.com/twoFiveOneTen/MQTT_V5
MQTT Version 5.0:
http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.htm
MQTT 3.1.1协议中文版:
https://github.com/mcxiaoke/mqtt
MQTT Version 3.1.1:
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html