MQTT 是客户端服务器发布/订阅消息传输协议。它重量轻、开放、简单,并且设计易于实施。这些特性使其非常适合在许多情况下使用,包括受限制的环境,例如机器对机器 (M2M) 和物联网 (IoT) 环境中的通信,其中需要小代码足迹和/或网络带宽非常宝贵。
该协议通过 TCP/IP 或其他提供有序、无损、双向连接的网络协议运行。其特点包括:
字节中的位标记为 7 到 0。第 7 位是最高有效位,最低有效位分配给第 0 位。
整数数据值是大端顺序的 16 位:高位字节在低位字节之前。这意味着 16 位字在网络上显示为最高有效字节 (MSB),然后是最低有效字节 (LSB)。
举例: 16位整数1的编码值为0x00 0x01,高位0x00在前,低位0x01在后。
编码规则:
在 UTF-8 中,U+0000…U+10FFFF 范围内的字符(UTF-16 可访问范围)使用 1 到 4 个八位字节的序列进行编码。
特别注意:收到以下范围内的编码数据,服务端/客户端必须关闭链接。
- UTF-8 编码的字符串不得包括 U+D800 和 U+DFFF 之间的编码
- UTF-8 编码的字符串不得包含空字符 U+0000 的编码
- U+0001…U+001F 控制字符
- U+007F…U+009F 控制字符
- Unicode 规范中定义的代码点[ Unicode ]是非字符(例如 U+0FFFF)
编码后的UTF-8数据:
0x41 0xF0 0xAA 0x9B 0x94
转换为二进制---->
0100 0001 1111 0000 1010 1010 1001 1011 1001 0100
(0x41) (0xF0) (0xAA) (0x9B) (0x94)
Utf-8编码解析---->
0100 0001 11110 ‾ \underline{1111 0} 11110 000 10 ‾ \underline{10} 10 10 1010 10 ‾ \underline{10} 10 01 1011 10 ‾ \underline{10} 10 01 0100
解析规则:
根据UTF-8字符串编码规则:
0x41:字节的高位为0,则剩余7位为有效数据位,其值即为原值0x41
0xF0:字节的高4位为1111,说明UTF-8编码字符串的长度为4字节,11110为编码前缀,后续的000为有效数据位,后续的字节10为编码前缀,后6位为有效数据位,则其值为0 0010 1010 0110 1101 0100,其16进制表示为0x2A6D4
综上,原始值为0x2A6D4
这些字段尽可能使用单字节编码UTF-8,便于编解码。
MQTT 协议通过以定义的方式交换一系列 MQTT 控制数据包来工作。本节介绍这些数据包的格式。一个 MQTT 控制包最多由三个部分组成,始终按以下顺序排列,如图所示。
每个 MQTT 控制包都包含一个固定的标头。图 2.2 - 固定头格式说明了固定头格式。
位置:字节 1,位 7-4。
表示为 4 位无符号值,这些值在表 2.1 - 控制数据包类型中列出。
DUP
如果 DUP 标志设置为 0,则表明这是客户端或服务器第一次尝试发送此 MQTT PUBLISH 数据包。如果 DUP 标志设置为 1,则表明这可能是先前尝试发送数据包的重新传递。
当客户端或服务器尝试重新传递 PUBLISH 数据包[MQTT-3.3.1.-1]时,必须将 DUP 标志设置为 1 。对于所有 QoS 0 消息[MQTT-3.3.1-2] ,必须将 DUP 标志设置为 0 。
QoS
RETAIN
如果 RETAIN 标志设置为 1,在客户端发送给服务器的 PUBLISH 数据包中,服务器必须存储应用程序消息及其 QoS,以便可以将其传递给订阅匹配其主题名称的未来订阅者
位置:从字节 2 开始。
剩余长度是当前数据包中剩余的字节数,包括可变标头和有效负载中的数据。剩余长度不包括用于编码剩余长度的字节。
Remaining Length 使用可变长度编码方案进行编码,该方案使用单个字节表示最大为 127 的值。较大的值按如下方式处理。每个字节的最低七位对数据进行编码,最高位用于表示表示中有后续字节。因此,每个字节编码 128 个值和一个“连续位”。剩余长度字段中的最大字节数为 4。
编码规则C语言实现:
do {
code = X % 128;
X = X / 128;
if (X > 0)
{
code = code | 128;
}
else
{
code = code;
}
} while (X >0);
举例说明:十进制数 321编码后的十六进制序列为0xC1 0x02
计算过程:
X = 321
code = X % 128 = 65
X = X / 128 = 321 / 128 = 2
Byte1:code | 128 = 65 | 128 = 0xC1
code = X % 128 = 2 % 128 = 2
Byte2:code = 2
某些类型的 MQTT 控制数据包包含可变标头组件。它位于固定报头和有效负载之间。可变报头的内容因数据包类型而异。可变报头的数据包标识符字段在几种数据包类型中很常见。
许多控制数据包类型的可变报头组件包括一个 2 字节的数据包标识符字段。这些控制数据包是 PUBLISH(其中 QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。
订阅、取消订阅和发布(在 QoS > 0 的情况下)控制数据包必须包含非零的 16 位数据包标识符 [MQTT-2.3.1-1]。
如果 PUBLISH 数据包的 QoS 值设置为 0 [MQTT-2.3.1-5] ,则 PUBLISH 数据包不得包含数据包标识符。
PUBACK、PUBREC 或 PUBREL 数据包必须包含与最初发送的 PUBLISH 数据包相同的数据包标识符 [MQTT-2.3.1-6]。同样,SUBACK 和 UNSUBACK 必须包含分别在相应的 SUBSCRIBE 和 UNSUBSCRIBE 数据包中使用的数据包标识符 [MQTT-2.3.1-7]。
客户端和服务器相互独立地分配数据包标识符。因此,客户端服务器对可以使用相同的数据包标识符参与并发消息交换。
一些 MQTT 控制数据包包含一个有效负载作为数据包的最后部分,如第 3 章所述。在 PUBLISH 数据包的情况下,这是应用程序消息。表 2.6 - 包含有效负载的控制包列出了需要有效负载的控制包。
至此,MQTT总体协议框架大致梳理了一遍,后续再梳理具体的协议命令。
参考链接: