【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架

这是机器未来的第28篇文章

原文首发地址:http://t.csdn.cn/9Feq3

mqtt_sologon

1. 概述

MQTT 是客户端服务器发布/订阅消息传输协议。它重量轻、开放、简单,并且设计易于实施。这些特性使其非常适合在许多情况下使用,包括受限制的环境,例如机器对机器 (M2M) 和物联网 (IoT) 环境中的通信,其中需要小代码足迹和/或网络带宽非常宝贵。

该协议通过 TCP/IP 或其他提供有序、无损、双向连接的网络协议运行。其特点包括:

  • 使用发布/订阅消息模式,提供一对多的消息分发和应用程序的解耦。
  • 与有效负载内容无关的消息传输。
  • 消息传递的三种服务质量:
    • “最多一次”,根据操作环境的最大努力传递消息。可能会发生消息丢失。例如,此级别可用于环境传感器数据,其中单个读数是否丢失并不重要,因为下一个读数将很快发布。
    • “至少一次”,确保消息到达但可能出现重复。
    • “Exactly once”,保证消息只到达一次。例如,此级别可用于重复或丢失消息可能导致应用不正确费用的计费系统。
  • 最小化传输开销和协议交换以减少网络流量。
  • 发生异常断开时通知相关方的机制。

2. 数据编码规则

2.1 位

字节中的位标记为 7 到 0。第 7 位是最高有效位,最低有效位分配给第 0 位。

2.2 整数数据值

整数数据值是大端顺序的 16 位:高位字节在低位字节之前。这意味着 16 位字在网络上显示为最高有效字节 (MSB),然后是最低有效字节 (LSB)。

举例: 16位整数1的编码值为0x00 0x01,高位0x00在前,低位0x01在后。

2.3 UTF-8 编码字符串

  • UTF-8编码字符串的规则如下:
    【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第1张图片前面2个字节表示长度,长度的编码规则符合2.2的定义,UTF-8编码字符串符合 RFC3629的定义。
    【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第2张图片编码规则:
    在 UTF-8 中,U+0000…U+10FFFF 范围内的字符(UTF-16 可访问范围)使用 1 到 4 个八位字节的序列进行编码。

    • 只有一个“序列”的八位字节将高位设置为 0,其余 7 位用于对字符编号进行编码。
    • 在一个n 个八位字节的序列,n>1,初始八位位组有 n 个高阶位设置为 1,然后是位设置为 0。其余位该八位字节包含要成为的字符数中的位编码。随后八位字节都将高阶位设置为1 和后面的位设置为 0,每个保留 6 位包含来自要编码的字符的位。

特别注意:收到以下范围内的编码数据,服务端/客户端必须关闭链接。
- UTF-8 编码的字符串不得包括 U+D800 和 U+DFFF 之间的编码
- UTF-8 编码的字符串不得包含空字符 U+0000 的编码
- U+0001…U+001F 控制字符
- U+007F…U+009F 控制字符
- Unicode 规范中定义的代码点[ Unicode ]是非字符(例如 U+0FFFF)

示例:字母 A 后跟代码点 U+2A6D4的表示如下
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第3张图片编码后的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字符串的协议为
    • CONNECT协议的ClientID、遗嘱topic、username、
    • PUBLISH的Topic名称
    • UNSUBSCRIBE的主题过滤器

这些字段尽可能使用单字节编码UTF-8,便于编解码。

3. MQTT控制包格式

3.1 MQTT控制包结构

MQTT 协议通过以定义的方式交换一系列 MQTT 控制数据包来工作。本节介绍这些数据包的格式。一个 MQTT 控制包最多由三个部分组成,始终按以下顺序排列,如图所示。
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第4张图片

3.1.1 固定头

每个 MQTT 控制包都包含一个固定的标头。图 2.2 - 固定头格式说明了固定头格式。
在这里插入图片描述

3.1.1 MQTT控制包类型

位置:字节 1,位 7-4。
表示为 4 位无符号值,这些值在表 2.1 - 控制数据包类型中列出。
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第5张图片

3.1.1.2 标志

目前仅PUBLISH支持标志的配置,其余均为保留将来使用。
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第6张图片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,以便可以将其传递给订阅匹配其主题名称的未来订阅者

3.1.1.3 剩余长度

位置:从字节 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从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第7张图片

3.1.2 可变数据头

某些类型的 MQTT 控制数据包包含可变标头组件。它位于固定报头和有效负载之间。可变报头的内容因数据包类型而异。可变报头的数据包标识符字段在几种数据包类型中很常见。
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第8张图片许多控制数据包类型的可变报头组件包括一个 2 字节的数据包标识符字段。这些控制数据包是 PUBLISH(其中 QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK。

订阅、取消订阅和发布(在 QoS > 0 的情况下)控制数据包必须包含非零的 16 位数据包标识符 [MQTT-2.3.1-1]。

  • 每次客户端发送这些类型之一的新数据包时,它必须为其分配一个当前未使用的数据包标识符 [MQTT-2.3.1-2]。
  • 如果客户端重新发送一个特定的控制包,那么它必须在该包的后续重新发送中使用相同的包标识符。在客户端处理完相应的确认数据包后,数据包标识符就可以重新使用了。
  • 在 QoS 1 PUBLISH 的情况下,这是相应的 PUBACK;在 QoS 2 的情况下,它是 PUBCOMP。
  • 对于 SUBSCRIBE 或 UNSUBSCRIBE,它是对应的 SUBACK 或 UNSUBACK [MQTT-2.3.1-3]。
  • 当服务器发送具有 QoS > 0 [MQTT-2.3.1-4]的 PUBLISH 时,相同的条件适用于服务器。

如果 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从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第9张图片3.1.3 有效载荷

一些 MQTT 控制数据包包含一个有效负载作为数据包的最后部分,如第 3 章所述。在 PUBLISH 数据包的情况下,这是应用程序消息。表 2.6 - 包含有效负载的控制包列出了需要有效负载的控制包。
【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架_第10张图片

至此,MQTT总体协议框架大致梳理了一遍,后续再梳理具体的协议命令。

参考链接:

  • MQTT Version 3.1.1 Plus Errata 01

写在末尾:

  • 博客简介:专注AIoT领域,追逐未来时代的脉搏,记录路途中的技术成长!
  • 专栏简介:从0到1掌握MQTT分布式协议。
  • 面向人群:零基础编程爱好者
  • 专栏计划:接下来会逐步发布跨入人工智能的系列博文,敬请期待
    • Python零基础快速入门系列
    • 快速入门Python数据科学系列
    • 人工智能开发环境搭建系列
    • 机器学习系列
    • 物体检测快速入门系列
    • 自动驾驶物体检测系列

你可能感兴趣的