MQTT 数据包格式简介(V5.0)

MQTT 数据包格式简介(V5.0)

前言

作为一种IOT领域的网络协议,MQTT需要在实现功能的前提下尽可能的精炼协议的长度,因此它的数据包格式设计的很巧妙。我根据MQTT规范 MQTT Specifications MQTT Version 5.0,做了一些翻译和总结来进行学习和归纳。

概览

MQTT的数据包格式可以划分为以下三部分。

数据类型 出现频率
固定头(Fixed Header) 所有类型
可变头(Variable Header) 部分类型
载荷(Payload) 部分类型

固定头(Fixed Header)

bit 7-4 3-0
Byte1 包类型 每种包类型的特殊标志位
Byte2... 剩余长度(第7-4bit) 剩余长度(第3-0bit)
  • 包类型由4个bit组成,有16个值,各值含义如下
名字 传输方向 描述
Reserved 0 禁止 保留
CONNECT 1 客户端->服务端 连接请求
CONNACK 2 服务端->客户端 连接确认
PUBLISH 3 客户端->服务端 或 服务端->客户端 发布消息
PUBACK 4 客户端->服务端 或 服务端->客户端 发布确认(QoS 1)
PUBREC 5 客户端->服务端 或 服务端->客户端 发布接收确认(QoS 2 part1)
PUBREL 6 客户端->服务端 或 服务端->客户端 发布释放(QoS 2 part2)
PUBCOMP 7 客户端->服务端 或 服务端->客户端 发布完成(QoS 2 part3)
SUBSCRIBE 8 客户端->服务端 请求订阅
SUBACK 9 服务端->客户端 订阅确认
UNSUBSCRIBE 10 客户端->服务端 取消订阅请求
UNSUBACK 11 服务端->客户端 取消订阅确认
PINGREQ 12 客户端->服务端 ping请求
PINGRESP 13 服务端->客户端 ping响应
DISCONNECT 14 客户端->服务端 或 服务端->客户端 连接断开通知
AUTH 15 客户端->服务端 或 服务端->客户端 认证交换
  • 每种包类型的特殊标志位由4个bit组成,有16个值,下面列出涉及的包类型的以及保留值特殊的包类型,未提及的包类型所有特殊标志位均要设置为0
包类型 是否使用 bit3 bit2 bit1 bit0
PUBLISH 在v5.0版本中使用 DUP Qos(bit1) Qos(bit0) RETAIN
PUBREL 保留 0 0 1 0
SUBSCRIBE 保留 0 0 1 0
UNSUBSCRIBE 保留 0 0 1 0

DUP = PUBLISH包是否重复

QoS = PUBLISH服务质量

RETAIN = PUBLISH是否在服务器上保存

  • 从Byte2开始为剩余长度,它是一个可变字节数,用于表示这个MQTT数据包中还有多少个字节(不包括它本身),即可变头加载荷的长度。通过这个定义可以得出,MQTT数据包的总长度 = 固定头的长度 + 剩余长度

可变头(Variable Header)

可变头具体含义取决于包类型,但在MQTT 5.0协议中,大部分包类型都含有两部分,包序号(Packet Identifier)和特性(Properties)。

包序号(Packet Identifier)

包序号为双字节数,使用了包序号的包类型有。

包类型 是否含有包序号
CONNECT
CONNACK
PUBLISH 是(当 QoS > 0 时)
PUBACK
PUBREC
PUBCOMP
SUBSCRIBE
UNSUBSCRIBE
PINGREQ
PINGRESP
DISCONNECT
AUTH

包序号的使用有如下规则:

  • 如果QoS为0的PUBLISH包不允许包含包序号
  • 每当客户端发送一个新的SUBSCRIBE、UNSUBSCRIBE或PUBLISH(当 QoS > 0 时)包,它的包序号必须是一个未被使用的非0序号,同样地,服务端发送PUBLISH(当 QoS > 0 时)包时也要遵循此规则。
  • 如果发送方接受到了一个包相关联的确认包(包序号一致),那么这个包的包序号可以被重用。这里的相关联指 PUBLISH(QoS1)<->PUBACK、PUBLISH(QoS2)<->PUBCOMP(返回码大于等于128)和PUBREC(返回码大于等于128)、SUBSCRIBE<->SUBACK、SUBSCRIBE<->UNSUBACK。
  • 一次会话的包序号集合由发起方来维护,并且客户端和服务端分开维护的,这意味着客户端发送的包序号有可能与服务端发送的包序号重复。但一个发起方多条指令任何时候不能使用同一个包序号。

1_包序号时序图

特性(Properties)

特性是在MQTT V5.0协议中新增的重要特性。主要是为了解决V3版本协议扩展性不足的问题。这个特性在协议中扮演的角色类似HTTP协议中的HTTP头。特性用途的介绍可以看这篇博客 What are MQTT User Properties? – MQTT 5 Essentials Part 6

目前几乎所有的包类型(除了PINGREQ、PINGRESP)都包含此区域,额外的,CONNECT还有一些可选的特性位于载荷的遗言特性(Will Properties)区域。

特性区域起始字节为可变字节数的特性长度(Property Length),它表示它之后特性区域的长度,即不包括它本身,如果这个包不携带特性,那么它需要被设置成0。

特性长度后就是有效特性区域,这个区域可以多个特性,每个特性都是由一个为可变字节数的特性序号和特性值组成,多个特性之间的排列顺序并没有特别的要求。

V5.0协议预设了许多特性在各种包类型中,其中38(0x26)允许用户自定义自己需要的内容

序号 名词 (用途) 数据类型 包类型 / 遗言特性
1 0x01 Payload Format Indicator Byte PUBLISH, Will Properties
2 0x02 Message Expiry Interval Four Byte Integer PUBLISH, Will Properties
3 0x03 Content Type UTF-8 Encoded String PUBLISH, Will Properties
8 0x08 Response Topic UTF-8 Encoded String PUBLISH, Will Properties
9 0x09 Correlation Data Binary Data PUBLISH, Will Properties
11 0x0B Subscription Identifier Variable Byte Integer PUBLISH, SUBSCRIBE
17 0x11 Session Expiry Interval Four Byte Integer CONNECT, CONNACK, DISCONNECT
18 0x12 Assigned Client Identifier UTF-8 Encoded String CONNACK
19 0x13 Server Keep Alive Two Byte Integer CONNACK
21 0x15 Authentication Method UTF-8 Encoded String CONNECT, CONNACK, AUTH
22 0x16 Authentication Data Binary Data CONNECT, CONNACK, AUTH
23 0x17 Request Problem Information Byte CONNECT
24 0x18 Will Delay Interval Four Byte Integer Will Properties
25 0x19 Request Response Information Byte CONNECT
26 0x1A Response Information UTF-8 Encoded String CONNACK
28 0x1C Server Reference UTF-8 Encoded String CONNACK, DISCONNECT
31 0x1F Reason String UTF-8 Encoded String CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT, AUTH
33 0x21 Receive Maximum Two Byte Integer CONNECT, CONNACK
34 0x22 Topic Alias Maximum Two Byte Integer CONNECT, CONNACK
35 0x23 Topic Alias Two Byte Integer PUBLISH
36 0x24 Maximum QoS Byte CONNACK
37 0x25 Retain Available Byte CONNACK
38 0x26 User Property UTF-8 String Pair CONNECT, CONNACK, PUBLISH, Will Properties, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, DISCONNECT, AUTH
39 0x27 Maximum Packet Size Four Byte Integer CONNECT, CONNACK
40 0x28 Wildcard Subscription Available Byte CONNACK
41 0x29 Subscription Identifier Available Byte CONNACK
42 0x2A Shared Subscription Available Byte CONNACK

载荷(Payload)

部分MQTT数据包的最后一部分就是载荷,在PUBLISH包中,这个是应用信息。

包类型 是否需要
CONNECT 需要
CONNACK 不需要
PUBLISH 可选
PUBACK 不需要
PUBREC 不需要
PUBREL 不需要
PUBCOMP 不需要
SUBSCRIBE 需要
SUBACK 需要
UNSUBSCRIBE 需要
UNSUBACK 需要
PINGREQ 不需要
PINGRESP 不需要
DISCONNECT 不需要
AUTH 不需要

原因码(Reason Code)

原因码是一个字节的无符号值用于表示操作的结果。一般表示成功的原因码为0,不大于0x80也表示成功,但会表示操作成功的不同结果,而大于等于0x80表示失败。

CONNACK,PUBACK,PUBREC,PUBREL,PUBCOMP,DISCONNECT,AUTH包中有一个单独的原因码在可变头中,SUBACK和UNSUBACK包中则是在载荷中有一连串的1个或多个原因码。

原因码 名称 包类型
0 0x00 Success CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH
0 0x00 Normal disconnection DISCONNECT
0 0x00 Granted QoS 0 SUBACK
1 0x01 Granted QoS 1 SUBACK
2 0x02 Granted QoS 2 SUBACK
4 0x04 Disconnect with Will Message DISCONNECT
16 0x10 No matching subscribers PUBACK, PUBREC
17 0x11 No subscription existed UNSUBACK
24 0x18 Continue authentication AUTH
25 0x19 Re-authenticate AUTH
128 0x80 Unspecified error CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT
129 0x81 Malformed Packet CONNACK, DISCONNECT
130 0x82 Protocol Error CONNACK, DISCONNECT
131 0x83 Implementation specific error CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT
132 0x84 Unsupported Protocol Version CONNACK
133 0x85 Client Identifier not valid CONNACK
134 0x86 Bad User Name or Password CONNACK
135 0x87 Not authorized CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT
136 0x88 Server unavailable CONNACK
137 0x89 Server busy CONNACK, DISCONNECT
138 0x8A Banned CONNACK
139 0x8B Server shutting down DISCONNECT
140 0x8C Bad authentication method CONNACK, DISCONNECT
141 0x8D Keep Alive timeout DISCONNECT
142 0x8E Session taken over DISCONNECT
143 0x8F Topic Filter invalid SUBACK, UNSUBACK, DISCONNECT
144 0x90 Topic Name invalid CONNACK, PUBACK, PUBREC, DISCONNECT
145 0x91 Packet Identifier in use PUBACK, PUBREC, SUBACK, UNSUBACK
146 0x92 Packet Identifier not found PUBREL, PUBCOMP
147 0x93 Receive Maximum exceeded DISCONNECT
148 0x94 Topic Alias invalid DISCONNECT
149 0x95 Packet too large CONNACK, DISCONNECT
150 0x96 Message rate too high DISCONNECT
151 0x97 Quota exceeded CONNACK, PUBACK, PUBREC, SUBACK, DISCONNECT
152 0x98 Administrative action DISCONNECT
153 0x99 Payload format invalid CONNACK, PUBACK, PUBREC, DISCONNECT
154 0x9A Retain not supported CONNACK, DISCONNECT
155 0x9B QoS not supported CONNACK, DISCONNECT
156 0x9C Use another server CONNACK, DISCONNECT
157 0x9D Server moved CONNACK, DISCONNECT
158 0x9E Shared Subscriptions not supported SUBACK, DISCONNECT
159 0x9F Connection rate exceeded CONNACK, DISCONNECT
160 0xA0 Maximum connect time DISCONNECT
161 0xA1 Subscription Identifiers not supported SUBACK, DISCONNECT
162 0xA2 Wildcard Subscriptions not supported SUBACK, DISCONNECT

名词解释

双字节(Two Byte Integer)、四字节数(Four Byte Integer)

MQTT中使用的多字节数均为大端序

可变字节数 (Variable Byte Integer)

可变字节是一种编码方式,将每个字节的最高位定义为“延续位”(continuation bit),剩下7位存储数据。“延续位”为1说明后一个字节也属于这个可变字节数。

例如,127可以用一个字节表示为0x7F。而128由于超过了7个位能表达的最大值,因此它需要两个字节表示。第1个字节的最高位“延续位”为1,剩下的7位存储128的0-7位,即全部为0,因此第一个字节为0x80,而第二个字节后不需要更多字节了,所以它的最高位“延续位”为0,剩下的7位存储128的8-14位,也就是1,所以第二个字节位0x01。综上所述,128使用可变字节数来表示,就是0x80,0x01。

目前,可变字节数最多可以由4个字节组成,那么根据规则,可以给出1-4个字节数的取值范围。

字节数 起始 终止
1 0 (0x00) 127 (0x7F)
2 128 (0x80,0x01) 16383 (0xFF,0x7F)
3 16,384 (0x80, 0x80, 0x01) 2,097,151 (0xFF, 0xFF, 0x7F)
4 2,097,152 (0x80, 0x80, 0x80, 0x01) 268,435,455 (0xFF, 0xFF, 0xFF, 0x7F)

MQTT协议中给出了非规范性建议的编解码实现的伪代码如下。

// 编码
// X 为待编码数
// encodeByte 为当前正在编码的字节
do
    encodeByte = X MOD 128
    X = X DIV 128
    if (X > 0)
        encodeByte = encodeByte OR 128
    endif
    'output' encodeByte
while (X > 0)
// 解码
// value 为解码后得到的数
multiplier = 1
value = 0
do
    encodedByte = 'next byte from stream'
    value += (encodedByte AND 127) * multiplier
    if (multiplier > 128*128*128)
        throw Error(Malformed Variable Byte Integer)
    multiplier *= 128
while ((encodedByte AND 128) != 0)

二进制数据 (Binary Data)

二进制数据起始为双字节数表示长度(0 ~ 65536),后面跟着这个长度的字节。

UTF-8 编码字符串 (UTF-8 Encoded String)

在有效数据前有一个双字节数用于表示字符串长度(0 ~ 65535),有效数据按照UTF-8规则编码,协议中要求不能包含控制字符(U+0001..U+001F、U+007F..U+009F)、不能包含空字符(U+0000)等,这里不在赘述,具体请查看这里

以字符串“A𪛔(U+2A6D4)”举例,

字节 描述
Byte1 0 字符串长度高字节(MSB)
Byte2 5 字符串长度低字节(LSB)
Byte3 'A'(0x41)
Byte4 0xF0
Byte5 0xAA
Byte6 0x9B
Byte7 0x94

UTF-8 编码字符串对 (UTF-8 String Pair)

两个UTF-8 编码字符串组成的键-值对。

参考来源

[1] MQTT Specifications MQTT Version 5.0: https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html

[2] MQTT.org: https://mqtt.org/

[3] What are MQTT User Properties? – MQTT 5 Essentials Part 6: https://www.hivemq.com/blog/mqtt5-essentials-part6-user-properties/

posted @ 2024-04-26 16:03  Xsuns  阅读(1399)  评论(0)    收藏  举报