一. 什么是MQTT协议
MQTT是一种基于标准的消息传递或规则集,用于机器对机器的通信。智能传感器、可穿戴设备和其他物联网(IoT)设备通常必须通过带宽有限的资源受限网络传输和接收数据。这些物联网设备使用MQTT进行数据传输,因为它易于实施,并且可以有效地传输物联网数据。MQTT支持设备到云端和云端到设备之间的消息传递。
二. 有什么优点
轻量、高效
IoT设备上的MQTT实施需要最少的资源,因此它甚至可以用于小型微控制器。例如,最小的MQTT控制消息可以少至两个数据字节。MQTT消息的标头也很小,因此可以优化网络带宽。
可扩展
MQTT实施需要最少的代码,在操作中消耗的功率非常少。该协议还具有支持与大量物联网设备通信的内置功能。因此,可以实施MQTT协议来连接数百万台此类设备。
可靠
许多 IoT 设备通过低带宽、高延迟的不可靠蜂窝网络连接。MQTT 具有内置功能,可减少 IoT 设备重新连接云所需的时间。它还定义了三种不同的服务质量级别,以确保 IoT 用例的可靠性——最多一次(0)、至少一次(1)和恰好一次(2)。
安全
MQTT使开发人员可以轻松地使用现代身份验证协议(例如OAuth、TLS1.3、客户管理的证书等)加密消息并对设备和用户进行身份验证。
得到良好的支持
几种语言(如Python)对MQATT协议的实施提供广泛的支持。因此,开发人员可以在任何类型的应用程序中以最少的编码快速实现它。
三. MQTT的原理
MQTT 协议基于发布/订阅模型工作。在传统的网络通信中,客户端和服务器直接相互通信。客户端向服务器请求资源或数据,然后,服务器将处理并发回响应。但是,MQTT 使用发布/订阅模式将消息发送者(发布者)与消息接收者(订阅者)解耦。相反,称为消息代理的第三个组件将处理发布者和订阅者之间的通信。代理的工作是筛选所有来自发布者的传入消息,并将它们正确地分发给订阅者。代理将发布者和订阅者解耦,如下所示:
空间解耦
发布者和订阅者不知道彼此的网络位置,也不交换 IP 地址或端口号等信息。
时间解耦
发布者和订阅者不会同时运行或具有网络连接。
同步解耦
发布者和订阅者都可以发送或接收消息,而不会互相干扰。例如,订阅者不必等待发布者发送消息。
四. MQTT有哪些组件
MQTT 通过如下定义客户端和代理来实施发布/订阅模型。
MQTT 客户端
MQTT 客户端是从服务器到运行 MQTT 库的微控制器的任何设备。如果客户端正在发送消息,它将充当发布者;如果它正在接收消息,它将充当接收者。基本上,任何通过网络使用 MQTT 进行通信的设备都可以称为 MQTT 客户端设备。
MQTT 代理
MQTT 代理是协调不同客户端之间消息的后端系统。代理的职责包括接收和筛选消息、识别订阅每条消息的客户端,以及向他们发送消息。它还负责其他任务,例如:
- 授权 MQTT 客户端以及对其进行身份验证
- 将消息传递给其他系统以进行进一步分析
- 处理错过的消息和客户端会话
MQTT 连接
客户端和代理开始使用 MQTT 连接进行通信。客户端通过向 MQTT 代理发送 CONNECT 消息来启动连接。代理通过响应 CONNACK 消息来确认已建立连接。MQTT 客户端和代理都需要 TCP/IP 堆栈进行通信。客户端从不相互联系,它们只与代理联系。
五. MQTT工作原理
下面概述了 MQTT 的工作原理。
- MQTT 客户端与 MQTT 代理建立连接。
- 连接后,客户端可以发布消息、订阅特定消息或同时执行这两项操作。
- MQTT 代理收到一条消息后,会将其转发给对此感兴趣的订阅者。
让我们进行详细的分解,以进一步了解详情。
MQTT 主题
“主题”一词是指 MQTT 代理用于为 MQTT 客户端筛选消息的关键字。主题是分层组织的,类似于文件或文件夹目录。例如,考虑在多层房屋中运行的智能家居系统,每层都有不同的智能设备。在这种情况下,MQTT 代理可以将主题组织为:
ourhome/groundfloor/livingroom/light
ourhome/firstfloor/kitchen/temperature
MQTT publish
MQTT 客户端以字节格式发布包含主题和数据的消息。客户端确定数据格式,例如文本数据、二进制数据、XML 或 JSON 文件。例如,智能家居系统中的灯可能会针对主题 livingroom/light 发布消息 on。
MQTT subscribe
MQTT 客户端向 MQTT 代理发送 SUBSCRIBE 消息,以接收有关感兴趣主题的消息。此消息包含唯一标识符和订阅列表。例如,您手机上的智能家居应用程序想要显示您家中有多少灯亮着。它将订阅主题 light 并增加所有 on 消息的计数器。
六. QoS(Quality of Service levels)
服务质量是 MQTT 的一个重要特性。当我们使用 TCP/IP 时,连接已经在一定程度上受到保护。但是在无线网络中,中断和干扰很频繁,MQTT 在这里帮助避免信息丢失及其服务质量水平。这些级别在发布时使用。如果客户端发布到 MQTT 服务器,则客户端将是发送者,MQTT 服务器将是接收者。当MQTT服务器向客户端发布消息时,服务器是发送者,客户端是接收者。
QoS 0
这一级别会发生消息丢失或重复,消息发布依赖于底层TCP/IP网络。即:<=1
QoS 1
QoS 1 承诺消息将至少传送一次给订阅者。
QoS 2
使用 QoS 2,我们保证消息仅传送到目的地一次。为此,带有唯一消息 ID 的消息会存储两次,首先来自发送者,然后是接收者。QoS 级别 2 在网络中具有最高的开销,因为在发送方和接收方之间需要两个流。
七. MQTT 数据包结构
- 固定头(Fixed header),存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识;
- 可变头(Variable header),存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容;
- 消息体(Payload),存在于部分MQTT数据包中,表示客户端收到的具体内容;
整体MQTT的消息格式如下图所示;
7.1 MQTT固定头
固定头存在于所有MQTT数据包中,其结构如下:
下面简单分析一下固定头的消息格式;
MQTT消息类型 / message type
**位置:**byte 1, bits 7-4。
4位的无符号值,类型如下:
名称 | 值 | 流方向 | 描述 |
---|---|---|---|
Reserved | 0 | 不可用 | 保留位 |
CONNECT | 1 | 客户端到服务器 | 客户端请求连接到服务器 |
CONNACK | 2 | 服务器到客户端 | 连接确认 |
PUBLISH | 3 | 双向 | 发布消息 |
PUBACK | 4 | 双向 | 发布确认 |
PUBREC | 5 | 双向 | 发布收到(保证第1部分到达) |
PUBREL | 6 | 双向 | 发布释放(保证第2部分到达) |
PUBCOMP | 7 | 双向 | 发布完成(保证第3部分到达) |
SUBSCRIBE | 8 | 客户端到服务器 | 客户端请求订阅 |
SUBACK | 9 | 服务器到客户端 | 订阅确认 |
UNSUBSCRIBE | 10 | 客户端到服务器 | 请求取消订阅 |
UNSUBACK | 11 | 服务器到客户端 | 取消订阅确认 |
PINGREQ | 12 | 客户端到服务器 | PING请求 |
PINGRESP | 13 | 服务器到客户端 | PING应答 |
DISCONNECT | 14 | 客户端到服务器 | 中断连接 |
Reserved | 15 | 不可用 | 保留位 |
标识位 / DUP
**位置:**byte 1, bits 3-0。
在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接:
数据包 | 标识位 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | 保留位 | 0 | 0 | 0 | 0 |
CONNACK | 保留位 | 0 | 0 | 0 | 0 |
PUBLISH | MQTT 3.1.1使用 | DUP1 | QoS2 | QoS2 | RETAIN3 |
PUBACK | 保留位 | 0 | 0 | 0 | 0 |
PUBREC | 保留位 | 0 | 0 | 0 | 0 |
PUBREL | 保留位 | 0 | 0 | 0 | 0 |
PUBCOMP | 保留位 | 0 | 0 | 0 | 0 |
SUBSCRIBE | 保留位 | 0 | 0 | 0 | 0 |
SUBACK | 保留位 | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | 保留位 | 0 | 0 | 0 | 0 |
UNSUBACK | 保留位 | 0 | 0 | 0 | 0 |
PINGREQ | 保留位 | 0 | 0 | 0 | 0 |
PINGRESP | 保留位 | 0 | 0 | 0 | 0 |
DISCONNECT | 保留位 | 0 | 0 | 0 | 0 |
- DUP:发布消息的副本。用来在保证消息的可靠传输,如果设置为
1,则在下面的变长中增加MessageId,并且需要回复确认,以保证消息传输完成,但不能用于检测消息重复发送。
- QoS发布消息的服务质量(前面已经做过介绍),即:保证消息传递的次数
- 00:最多一次,即:<=1
- 01:至少一次,即:>=1
- 10:一次,即:=1
- 11:预留
- RETAIN:发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果设有那么推送至当前订阅者后释放。
剩余长度(Remaining Length)
位置:byte 1。
固定头的第二字节用来保存变长头部和消息体的总大小的,但不是直接保存的。这一字节是可以扩展,其保存机制,前7位用于保存长度,后一部用做标识。当最后一位为 1时,表示长度不足,需要使用二个字节继续保存。例如:计算出后面的大小为0
7.2 MQTT可变头 / Variable header
MQTT数据包中包含一个可变头,它驻位于固定的头和负载之间。可变头的内容因数据包类型而不同,较常的应用是做为包的标识:
Bit | 7 — 0 |
---|---|
byte 1 | 包标签符(MSB) |
byte 2… | 包标签符(LSB) |
很多类型数据包中都包括一个2字节的数据包标识字段,这些类型的包有:
PUBLISH (QoS > 0)、PUBACK、PUBREC、PUBREL、PUBCOMP、
SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK
7.3 Payload消息体
Payload消息体是MQTT数据包的第三部分,CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息 有消息体:
CONNECT,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码
SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS。
SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
UNSUBSCRIBE,消息体内容是要订阅的主题。