在本教程中,我们将更详细地介绍MQTT协议,以及MQTT消息或数据包的格式。
我们将研究:
MQTT消息格式。
MQTT消息头
消息字段和编码
控制消息编码示例
介绍
MQTT是基于二进制的协议,控制元素是二进制字节而不是文本字符串。
MQTT使用命令和命令确认格式。
这意味着每个命令都有一个对应的确认。
主题名称,客户端ID,用户名和密码被编码为UTF-8字符串。
除了MQTT协议信息(例如客户端ID等)外,有效负载是二进制数据,内容和格式是特定于应用程序的。
MQTT数据包或消息格式由2字节固定报头(始终存在)+可变报头(并非始终存在)+有效负载(并非始终存在)组成。
可能的数据包格式为:
- Fixed Header (Control field + Length) – Example CONNACK
- Fixed Header (Control field + Length) + Variable Header -Example PUBACK
- Fixed Header (Control field + Length) + Variable Header + payload -Example CONNECT
固定报头字段由控制字段和可变长度数据包长度字段组成。
数据包长度字段的最小大小为1个字节,用于总长度小于127个字节的消息(不包括控制字段和长度字段)。
最大数据包大小为256MB。 小于127字节的小数据包具有1字节的数据包长度字段。
大于127且小于16383的数据包将使用2个字节。 等等
注意:只是用7位,第8位用作连续位.
Note: 7 bits are used with the 8th bit being a continuation bit.
最小数据包大小只有2个字节,其中包含一个单字节控制字段和一个单字节数据包长度字段。 例如,断开连接消息只有2个字节。
控制字段
8位控制字段是2字节固定报头的第一个字节。 它分为两个4位字段,包含所有协议命令和响应。
前4个最高有效位是命令或消息类型字段,其他4位用作控制标志。
下表摘自MQTT 3.1.1规范,并显示了MQTT命令及其相关代码的示例。
因为它们是8位字节字段中最重要的部分,所以我也以十进制显示了它们的字节值,就像它们出现在数据包中一样。
控制标志
尽管有16种可能的标志,但实际上只使用其中少部分。
发行消息充分利用了这些标志,如下表所示:
当使用QOS或1或2重新发布消息时使用重复标志
发布消息时使用QOS标志,并指示QOS级别-0,1,2
保留消息标志也用于发布。
长度字段(剩余长度)
它的长度在1到4个字节之间。 每个字节使用7位作为长度,其中MSB用作连续标志。
剩余长度是长度字段后面的字节数,包括可变长度的头和有效负载,如下所示:
下面说明了长度为64和321字节的数据包的长度字段
剩余数据包长度64个字节仅需要1个字节:
321个字节的数据包长度需要2个字节的剩余长度字段:
从规范中获取的下表显示了数据包大小和数据包长度字段。
可变长度头
如前所述,可变长度头字段并不总是出现在MQTT消息中。
某些MQTT消息类型或命令要求使用此字段来携带其他控制信息。
可变长度报头字段是相似的,但对于所有消息类型而言都不相同。
MQTT连接和断开连接消息示例
作为说明,我们现在将查看连接消息的数据包详细信息。
下面是一个真实的客户端连接和断开连接示例,显示了已发送和已接收数据的实际字节值。
CONNECT控制代码= 0x10
CONNACK控制码= 0x20
MQTT数据包=控制+长度+协议名称+协议级别+连接标志+保持活动+有效负载
(MQTT packet =control + length + protocol name + Protocol Level +Connect Flags + keep alive +Payload)
注解:
- 注意连接(0x10)和连接确认(0x20)控制代码。
- 注意十六进制0x17或23字节的总长度,其中不包括控制字段和长度字段。 长度字段只有1个字节。
- 您还应该能够在发送的数据包中看到客户端ID(python_test)。
- 当查看实际的数据包字节时,Python会打印十六进制值,除非它可以匹配ASCII字符。在上面的示例中,keep alive字段为x00x3C,但显示为x00 <。 因为Ascii < = 0x3C
注解:
- 客户端ID字段作为有效内容的第一部分发送,而不是作为头部的一部分发送。
- 客户端ID后跟一个长度字段。
- 连接标志表明正在请求一个干净的会话(clean session)。
- 连接标志是“可变长度”标头的一部分,用于指示用户名,密码的存在与否,以及有效负载中的消息字段。 它还包含干净会话标志和Will QOS。
控制包摘要表
Control Packet | Variable Header | Payload |
CONNECT | Required | Required |
CONNACK | None | None |
PUBLISH | Required | Optional |
PUBACK | Required | None |
PUBREC | Required | None |
PUBREL | Required | None |
PUBCOMP | Required | None |
SUBSCRIBE | Required | Required |
SUBACK | Required | Required |
UNSUBSCRIBE | Required | Required |
UNSUBACK | Required | Required |
PINGREQ | None | None |
PINGRESP | None | None |
DISCONNECT | None | None |
Wireshark网络分析
为了回应读者有关TCP协议的问题,我创建了这张摘自Wireshark的屏幕截图。
它显示了一个MQTT客户端连接和发布(QOS 1)。 您可以清楚地看到总长度为58个字节的ACK数据包。
我们知道ACK数据包是2个字节。
因此,没有MQTT的TCP数据包约为56个字节。
还有一点需要注意的有趣的事情是直到我完成数据包捕获之前我都没有想到的是,每个MQTT命令或响应都将获得一个TCP ACK,甚至还有一个MQTT ACK。
如果查看屏幕截图,则MQTT连接可以获取TCP ACK响应和MQTT Connect ACK响应。
MQTT Connect ACK响应会有一个TCP ACK响应。
参考:MQTT V3.1.1 Specification pdf
http://www.steves-internet-guide.com/mqtt-protocol-messages-overview/