1 概述
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
注:由于业务用上了MQTT作为设备通讯协议内容,此篇文章为总结几篇mqtt报文解析的好文再加上mqtt官方文档出的,故此为整理备忘文。
2 控制报文格式
MQTT控制报文由三部分组成:固定头(Fixed header)+可变头(Variable header)+有效载荷(Payload)
Fixed header | 固定头部,MQTT协议分很多种类型,如连接,发布,订阅,心跳等。其中固定头是必须的,所有类型的MQTT协议中,都必须包含固定头 |
Variable header | 可变头部,可变头部不是可选的意思,而是指这部分在有些协议类型中存在,在有些协议中不存在 |
Payload | 消息载体,就是消息内容。与可变头一样,在有些协议类型中有消息内容,有些协议类型中没有消息内容 |
2.2 固定报头(Fixed header)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT Control Packet type MQTT控制报文类型 | Flags specific to each MQTT Control Packet type 用于指定的控制报文类型标志位 | ||||||
byte 2… | Remaining Length 剩余长度 |
2.2.1 MQTT控制报文类型(MQTT Control Packet type)
第一个字节的7-4位,一共4位,可表示16个数字,除0 禁止使用以外,剩余15个数字各表示一个控制报文类型,如图所示:
Name 名称 | Value 值 | Direction of flow 报文流动方向 | Description 描述 |
Reserved | 0 | 禁止 | Reserved (预留) |
CONNECT | 1 | 客户端到服务器 | Client request to connect to Server 客户端连接到服务器 |
CONNACK | 2 | Server to Client | Connect acknowledgment 连接确认 |
PUBLISH | 3 | Client to Server or Server to Client | Publish message 发布消息 |
PUBACK | 4 | Client to Server or Server to Client | Publish acknowledgment 发布确认(QoS 1) |
PUBREC | 5 | Client to Server or Server to Client | Publish received (assured delivery part 1) 发布收到(QoS 2保证交付第一步) |
PUBREL | 6 | Client to Server or Server to Client | Publish release (assured delivery part 2) 发布释放(QoS 2保证交付第二步) |
PUBCOMP | 7 | Client to Server or Server to Client | Publish complete (assured delivery part 3) 发布完成(QoS 2保证交付第二步) |
SUBSCRIBE | 8 | Client to Server | Client subscribe request 客户端订阅请求 |
SUBACK | 9 | Server to Client | Subscribe acknowledgment 订阅请求确认 |
UNSUBSCRIBE | 10 | Client to Server | Unsubscribe request 取消订阅请求 |
UNSUBACK | 11 | Server to Client | Unsubscribe acknowledgment 取消订阅请求确认 |
PINGREQ | 12 | Client to Server | PING request 心跳请求 |
PINGRESP | 13 | Server to Client | PING response 心跳响应 |
DISCONNECT | 14 | Client to Server | Client is disconnecting 客户端断开连接 |
AUTH | 15 | Client to Server or Server to Client | Authentication exchange 身份验证交换 |
2.2.2 标志位(Flags)
固定报头中字节1的剩余位[3-0]包含特定于每个MQTT控制数据包类型的标志,如下所示。当标志位标记为“保留”时,它将保留供将来使用,并且必须设置为所列值[MQTT-2.1.3-1]。如果接收到无效标志,则为格式错误的数据包。
MQTT Control Packet | Fixed Header flags | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
CONNECT | Reserved | 0 | 0 | 0 | 0 |
CONNACK | Reserved | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT v5.0 | DUP | QoS | RETAIN | |
PUBACK | Reserved | 0 | 0 | 0 | 0 |
PUBREC | Reserved | 0 | 0 | 0 | 0 |
PUBREL | Reserved | 0 | 0 | 1 | 0 |
PUBCOMP | Reserved | 0 | 0 | 0 | 0 |
SUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
SUBACK | Reserved | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
UNSUBACK | Reserved | 0 | 0 | 0 | 0 |
PINGREQ | Reserved | 0 | 0 | 0 | 0 |
PINGRESP | Reserved | 0 | 0 | 0 | 0 |
DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
AUTH | Reserved | 0 | 0 | 0 | 0 |
DUP = Duplicate delivery of a PUBLISH packet(控制报文的重复分发标志)
QoS = PUBLISH Quality of Service( PUBLISH报文的服务质量等级)
RETAIN = PUBLISH retained message flag(PUBLISH报文的保留标志)
2.2.3 剩余长度(Remaining Length)
Remaining Length意思是剩余长度,即Variable Header + Payload的长度。剩余长度从Byte 2开始,最长可达4字节。所以剩余长度范围是Byte[2-5]。那么怎样确定其长度到底是1还是4呢,这取决于字节的最高位Bit 7(默认都是高字节在前),如果这个值是1,那么就继续计算字节长度,如果是0,那么就不再计算字节长度。
消息长度可以简单理解为128进制的数据,4位长度最大可以表示128*128*128*128Byte=256MB。但是这个长度的计算有些特别,就是低位在前,高位在后(因为正常的表示方法是高位在前,低位在后),字节最高位Bit7用于标记是否需要继续计算消息长度。以下是消息长度的长度范围:
字节 | 最小值 | 最大值 |
---|---|---|
1 | 0(0x00) | 127(0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (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) |
稍微注意一下,0x80=1000 0000,不是 1000。刚开始以为是1000,所以就没明白。
举个例子。
消息假设长度是[0X60],其二进制是01100000,字节最高位Bit7(从左边起第0位)是0,所以不需要继续往后计算。那么消息长度就是0X60,十进制数是96。
如果消息长度是[0XC1, 0XC2, 0X33],那么他们的二进制分别如下,
0xC1=1100 0001
0xC2=1100 0010
0x33=0011 0011,
第一字节最高位是1,那么需要继续向后计算,去掉标记位(0xC1%128),得到100 0001=41
第二字节最高位是1,那么需要继续向后计算,去掉标记位(0xC2%128),得到100 0010=42
第三字节最高位是0,不需要向后计算,其结果就是0x33=51
因为低位在前,高位在后,那么长度计算为Length=41 + 42*128 + 51*128*128=841001 B = 821KB
需要注意的是,消息长度=可变头部长度+消息内容长度。不包括首字节和消息长度本身,如果消息长度为5,那么说明这条消息后边还有5字节,整条消息长度为7(首字节+1位长度字节+5)。
另外如果消息长度为4字节,最后一位不能超过0X7F=127,因为如果超出这个值,其最高位Bit7是1,还需要往后计算,这与消息最大长度为4字节矛盾。所以如果出现[0XFF, 0XFF, 0XFF, 0XFF]这样的消息长度,那么接收方认为这是一条非法的消息。
2.3 可变报头(Variable header)
某些类型的MQTT控制数据包包含可变头组件。它位于固定标头和有效负载之间。可变报头的内容因数据包类型而异。可变报头的数据包标识符字段在几种数据包类型中很常见。
许多 MQTT 控制数据包类型的变量头组件包括一个两字节整数数据包标识符字段。这些 MQTT 控制数据包是 PUBLISH(其中 QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。
需要数据包标识符的 MQTT 控制数据包如下所示:
MQTT Control Packet | Packet Identifier field |
CONNECT | NO |
CONNACK | NO |
PUBLISH | YES (If QoS > 0) |
PUBACK | YES |
PUBREC | YES |
PUBREL | YES |
PUBCOMP | YES |
SUBSCRIBE | YES |
SUBACK | YES |
UNSUBSCRIBE | YES |
UNSUBACK | YES |
PINGREQ | NO |
PINGRESP | NO |
DISCONNECT | NO |
AUTH | NO |
如果 PUBLISH 数据包的 QoS 值设置为 0 ,则 PUBLISH 数据包不得包含数据包标识符。
每次客户端发送一个新的 SUBSCRIBE、UNSUBSCRIBE 或 PUBLISH(其中 QoS > 0)MQTT 控制数据包时,它必须为其分配一个当前未使用的非零数据包标识符 。
每次服务器发送一个新的 PUBLISH(QoS > 0)MQTT 控制数据包时,它必须为其分配一个当前未使用的非零数据包标识符 。
在发送方处理了相应的确认数据包后,数据包标识符就可以重新使用,定义如下。在 QoS 1 PUBLISH 的情况下,这是对应的 PUBACK;在 QoS 2 PUBLISH 的情况下,它是 PUBCOMP 或原因代码为 128 或更大的 PUBREC。对于 SUBSCRIBE 或 UNSUBSCRIBE,它是对应的 SUBACK 或 UNSUBACK。
与 PUBLISH、SUBSCRIBE 和 UNSUBSCRIBE 数据包一起使用的数据包标识符为会话中的客户端和服务器单独形成了一组统一的标识符。一个数据包标识符在任何时候都不能被多个命令使用。
PUBACK、PUBREC、PUBREL 或 PUBCOMP 数据包必须包含与最初发送的 PUBLISH 数据包相同的数据包标识符。SUBACK 和 UNSUBACK 必须包含分别在相应的 SUBSCRIBE 和 UNSUBSCRIBE 数据包中使用的数据包标识符。
客户端和服务器相互独立地分配数据包标识符。因此,客户端-服务器对可以使用相同的数据包标识符参与并发消息交换。
2.4 有效载荷(Payload)
一些MQTT控制数据包包含一个有效负载作为数据包的最后一部分。在发布包中,这是应用程序消息。控制类型所需 payload 如下图:
MQTT Control Packet | Payload |
CONNECT | Required |
CONNACK | None |
PUBLISH | Optional |
PUBACK | None |
PUBREC | None |
PUBREL | None |
PUBCOMP | None |
SUBSCRIBE | Required |
SUBACK | Required |
UNSUBSCRIBE | Required |
UNSUBACK | Required |
PINGREQ | None |
PINGRESP | None |
DISCONNECT | None |
AUTH | None |
参考文献:
1.MQTT协议 -- 消息报文格式_weixin_34166847的博客-CSDN博客
3.