想象你家里有智能灯泡、温度传感器、门锁等一堆智能设备,它们就像不会说话的 "哑巴",怎么让它们互相 "交流" 并听你指挥呢?MQTT 就是专门为这种场景设计的通信语言。
文章目录
前言
这篇文章将为大家详细介绍 MQTT 协议的控制报文,Qos 质量分数,主题。
一、MQTT 协议介绍
- 什么是MQTT ?
MQTT (Message Queuing Telemetry Transport,消息队列遥测传输)是一种基于发布 / 订阅
模式的轻量级即时通讯协议,专为资源受限设备和低带宽、高延迟或不可靠网络环境设计,广泛应用于物联网(IoT)、工业自动化、智能家居等领域。
常见的通讯模式:
- 客户端-服务器端:客户端和服务器端需要直接建立连接。
- 发布订阅模式:发布者和订阅者不需要直接建立连接。(通过服务器)
- MQTT 常见概念:
① MQTT 客户端 :任何运行MQTT客户端库(MQTT开发SDK)的应用或设备都是MQTT客户端。发布者和订阅者都是客户端。
② MQTT Broker : MQTT 服务器,负责接受客户端发布的消息,并根据订阅关系将消息传输到对应的客户端。实现了MQTT通讯协议的代理软件。
③ 主题 :是一种用于分类和过滤消息的字符串结构,决定了消息如何从发布者(publisher)流向订阅者(subscriber)。[当发布者主题和订阅者主题一致,订阅者即可通过服务器收到发布者发布的对应主题的消息]

- MQTT 协议特点:
- 轻量级:MQTT 协议占用的系统资源较少,数据报文较少。
- 可靠性强:提供了多种消息的质量等级。
- 安全性强:提供传输层和套阶层加密功能。
双向通讯
:MQTT 的客户端既可以发送数据也可以从服务器获取数据。- 多语言支持:PHP, Python, java...
- MQTTX 客户端软件介绍:
EMQX (MQTT Broker)是一款实现MQTT协议的开源的MQTT 消息代理软件。MQTT定义了消息通讯的规则和流程,而 EMQX 则是遵循这些规则的软件,使得设备能够按照 MQTT 协议进行有效的通讯。
MQTTX是EMQX开源的一款跨平台MQTT5.0客户端工具,它支持macOS,LinUx并且支持自定义脚本模拟测试、MQTT消息格式转换、日志记录等多个功能。
MQTTX 包合三种类型的工具:
1、MQTTX Desktop:MQTTX Desktop是一款跨平台的MQTT 桌面客户端工具。(较多使用MQTTX Desktop)
2、MQTTX CLI:MQTTX CLI是EMQ开源的一款 MQTT 5.0 命令行客户端工具。
3、MQTTWeb:MQTTX Web是一款基于浏览器访问客户端工具。
对于 MQTTX Desktop,首先添加pub, sub。需要设置名称(pub/sub),服务器地址(电脑IP),端口号,再连接。对于sub要去订阅主题。

二、MQTT 控制报文
报文 是网络中交换和传输数据的最小单元,通俗来讲就是站点一次性要发送的数据块。它包含了将要发送的完整数据信息,其长短不一致,长度不限且可变。MQTT客户端和服务端通过交换控制报文来完成它们的工作,比如订阅主题和发布消息。
MQTT 目前定义了 15 种控制报文的类型,如果按照功能分,可以将这些报文分为 连接,发布,订阅
三个类别:

- MQTT 报文格式:
在 MQTT 在,无论是什么类型的控制报文,它们都由 固定报头,可变报头,有效载荷 三部分组成。

PS : 固定报头固定存在于所有控制报文中 ,而可变报头和有效载荷是否存在以及它们的内容则取决于具体的报文类型。
例如 : 用于维持连接的 PINGREQ 报文就只有 一个固定报头,用于传递应用消息的 PUBLISH 报文则完整地包含了这 三个部分。
- 固定报头:
固定报头 由报文类型,标识位,报文剩余长度 三字段组成。
-
报文类型:
占 4 位,是一个无符号的整数 :
-
标识位:
占 4 个 bit 位,不过到MQTT5.0为止,只有 PUBLISH 报文的这四个比特位被赋予了明确的含义:
Bit3:DUP,表示当前PUBLISH报文是否是一个重传的报文。
Bit 2,1:QoS,表示当前PUBLISH报文使用的服务质量等级。
Bit0:Retain,表示当前PUBLISH报文是否是一个保留消息。
其他所有的报文中,这4位都仍是保留的。
- 剩余长度:
剩余长度 指示了当前控制报文剩余部分的字节数,也就是 可变报文和有效载荷 这两部分的长度。
MQTT 控制报文的总长度 = 固定报头的长度 + 剩余的长度。
- 可变报文
可变报文的内容取决于具体的报文类型。
例子:
CONNECT 报文的可变报头按顺序包含了协议名,协议级别,连接标识,Keep Alive 和属性这五个字段。
PUBLISH 报文的可变报文头按顺序包含了主题名,报文标识符和属性这三个字段。

属性是 MQTT 5.0 引入的一个概念,属性字段基本上是可变报头的最后一部分,有属性长度和紧随其后的一组属性组成,这里的属性长度指的是后面所有属性的总长度。

所有的属性都是可选的,因为他们通常都有一个默认值,如果没有任何属性,那么属性长度值就是 0。
- 有效载荷
有效载荷是用于实现对应报文的核心功能。
举例:
在PUBLISH报文中,Payload用于承载具体的应用消息内容,这也是PUBLISH报文最核心的功能。
在SUBSCRIBE报文中,PayLoad包含了想要订阅的主题以及对应的订阅选项,这也是SUBSCRIBE报文最主要的工作。
三、Qos 消息质量分数
MQTT 的服务质量(Quality of Service,QoS)是协议的核心特性之一,它定义了消息传递的可靠性级别。QoS 通过不同的确认机制和重传策略,确保消息在发送方和接收方之间以特定的可靠性级别传输。
- Qos 常见的取值:
0
:消息最多发送一次 -----> 可能会导致消息的丢失。
1
: 消息至少发送一次 -----> 肯定不会出现消息的丢失,消息可能会重复 。
2
: 消息仅有一次发送 -----> 消息肯定不回丢失,消息也不会重复。
在发送消息的时候可以指定消息的质量等级。一般情况下 服务器获取这个消息后,就会按照发送时所指定的消息质量等级 将消息发送给订阅者。
PS : 特殊情况下(当订阅者指定了 订阅时 消息的最大质量等级),消息质量等级会发生改变。
当 发布者 发布的 Qos 等于 2时,但是订阅者向服务器 指定了Qos 等于1,那么服务器转发给 订阅者的 Qos 就是1 而不是2。

-
Qos 等于 0:
Qos 0 是最低的 Qos 等级。 Qos 0 消息即发即弃,不需要等待确认,不需要存储和重传。因此对于接收方来说不需要担心收到重复的消息。
消息丢失原因:
当使用QoS0传递消息时,消息的可靠性完全依赖于底层的TCP协议。而TCP只能保证在连接稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭、重置,仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息。这也是QoS日消息最主要的丢失场景。
-
Qos 等于 1:
引入了应答和重传机制。
Qos 1 重传原因:(对于发送方来说,没收到PUBACK报文分为以下两种情况)
- PUBLISH未到达接收方
- PUBLISH已经到达接收方,接收方的PUBACK报文还未到达发送方
重传 PUBLISH 报文的时候,PUBLISH 中的 DUP 标志位被设置为 1,用来表示这是一个重传报文。
- Qos 等于 2:
Qos 2 解决了 Qos 1 消息可能丢失或者重复的问题,但是 带来了最复杂的交互流程和最高的开销。每一次 Qos 2 消息传递,都要求发送方与接收方进行 至少两次的请求 / 相应流程。
发送方 发送 PUBREL 后 则不会再进行消息的重传。
流程说明:
1、首先,发送方存储并发送QoS为2的PUBLISH报文以启动一次QoS2消息的传输,然后等待接收方回复PUBREC报文。这一部分与QoS1基本一致,只是响应报文从 PUBACK 变成了PUBREC。
2、当发送方收到PUBREC报文,即可确认对端已经收到了PUBLISH报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地存储的PUBLISH报文,然后发送一个PUBREL报文,通知对端自己准备将本次使用的PacketID标记为可用了。与PUBLISH报文一样,我们需要确保PUBREL报文到达对端,所以也需要一个响应报文,并且这个PUBREL报文需要被存储下来以便后续重传。
3、当接收方收到PUBREL报文,也可以确认在这一次的传输流程中不会再有重传的PUBLISH报文到达,因此回复PUBCOMP报文表示自己也准备好将当前的PacketID用于新的消息了。
4、当发送方收到PUBCOMP报文,这一次的QoS2消息传输就算正式完成了。在这之后,发送方可以再次使用当前的PacketID发送新的消息,而接收方再次收到使用这个PacketID的PUBLISH报文时,也会将它视为一个全新的消息。
四、主题讲解
- 主题介绍:
MQTT 主题本质上是一个UTF-8 编码的字符串,是MQTT 协议进行消息路由的基础。MQTT 主题类似 URL 路径,使用 / 进行分层。

为了避免歧义且易于理解,通常不建议主题以 / 开头或者结尾,例如:/ chat 或者 chat / .
MQTT 主题不需要提前创建。MQTT 客户端在订阅或者发布时即自动的创建了主题,开发者无需再关心主题的创建,并且要不需要收到的删除主题。
MQTT 主题通配符包含 单层通配符 + ,以及多层通配符 # ,主要用于客户端一次订阅多个主题。
- 单层通配符:
加号 (" + ")是用于 单个主题层级匹配的通配符。在使用单层通配符时,单层通配符必须占据整个层级,例如:

例子:如果客户端订阅了主题 test / + / temperature , 将会收到以下主题的消息:

但是不会匹配以下的主题:

- 多层通配符:
井字符号 (" # ")是用于匹配主题中任意层级的通配符。多层通配符表示它的父级和任意数量的子层级,在使用多层通配符时,它必须占据整个层级并且必须是主题的最后一个字符,例如:

如果客户端订阅主题 test / # , 它将会收到以下主题的消息:

总结
下篇文章为大家继续介绍 MQTT 的基础知识。