MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的”轻量级”通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 在 1999 年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
协议类型:发布/订阅(Pub/Sub)模式。
传输层协议:基于 TCP/IP 协议栈,默认使用 TCP 作为传输协议,但也有针对 WebSocket 和 TLS 等协议的扩展。
轻量级:头部非常小,适合在带宽受限、计算能力有限的嵌入式设备中使用。
MQTT协议特性
发布/订阅模型
- 发布者(Publisher):向特定的“主题(Topic)”发布消息。
- 订阅者(Subscriber):对一个或多个主题进行订阅,接收相关的消息。
- 代理(Broker):充当消息的中转站,管理客户端的连接、消息路由和交付。
这种模型使得通信双方不需要直接互相了解或联系,而是通过代理来传递消息,提升了系统的灵活性和扩展性。
消息传递
主题(Topic):消息的标识符,通常是一个层级结构的字符串。例如,home/livingroom/light。
QoS(Quality of Service)级别
:MQTT 提供了三种消息传递服务质量级别:
- QoS 0:最多一次传送(At most once),消息可能丢失,不会重发。
- QoS 1:至少一次传送(At least once),消息会重发,直到确认收到。
- QoS 2:只有一次传送(Exactly once),确保消息只发送一次。
持久化会话(Session Persistence)
- MQTT 允许在断开连接后保留客户端的会话状态,包括订阅信息和未接收的消息。连接恢复后,可以继续接收消息,而无需重新订阅。
遗嘱消息(Last Will and Testament, LWT)
- MQTT 允许在客户端异常断开时,代理发送一个遗嘱消息。这有助于检测客户端的状态,并通知其他订阅者。
低带宽和高效性
- MQTT 协议的消息头非常小,最大只有 2 字节,适合带宽受限或不稳定的网络环境。
MQTT协议包结构
MQTT(Message Queuing Telemetry Transport)协议结构主要由 固定报头(Fixed Header) 和 可变报头(Variable Header) 以及 有效载荷(Payload) 构成。可变头部与有效荷载不一定每个报文都有,内容根据报文类型不同而不同。
固定头部 | 1-2 字节 | 包括消息类型、标志和剩余长度。 |
可变头部 | 0 到 N 字节 | 包含主题名称、消息标识符等。 |
有效载荷 | N 字节 | 包含消息的实际数据。 |
固定报头
第一字节:
- 0-3 位:用于指定报文类型的标志位。
- 4-7 位:用于表示报文类型。
第二字节及后续字节:
- 剩余长度(Remaining Length):从第二字节开始,表示后续可变报头和消息负载的总长度。
- 剩余长度字段最多可以使用四个字节。
- 单个字节最大值为 0x7F(十六进制),即 127 字节。
- 如果剩余长度字段的最高位(第 8 位)为 1,则表示后续还有更多字节存在,这种机制被称为“延续位”。
- 因此,最后一字节的最大值只能是 0x7F。
固定报头第一个字节表
DUP1:用于标识消息是否是重复的。0:表示该消息是新的,1:表示该消息是重复的。
QoS2:定义消息传递的可靠性。MQTT协议提供了三个级别的QoS,分别为:
- QoS 0:至多一次(At most once)——消息最多被传输一次,不做重传保证。适用于不需要严格可靠性的场景。
- QoS 1:至少一次(At least once)——消息至少传输一次,保证消息到达。会进行重试以确保消息被接收。
- QoS 2:只有一次(Exactly once)——消息只会传输一次,并且通过四次握手保证消息的唯一性。适用于高可靠性需求的场景。
- 值:00:QoS 0、01:QoS 1、10:QoS 2
RETAIN3:指示消息是否应该被保留在代理服务器上,以便后续的新订阅者能够立即接收到这条消息。
如果设置了 RETAIN,则该消息会在代理中保留,直到有新的相同主题的消息发布。此时,新的订阅者一旦订阅该主题,会立即收到这条保留的消息。
值:0:表示消息不会被保留。1:表示消息将被保留。
可变报头与有效荷载
不同类型的报文可变报头与有效荷载均不相同,这里以 CONNECT 与 CONNACK 为例:
CONNECT 报文是用于客户端向服务器发起连接请求的报文,服务器会验证其中的信息并且返回 CONNACK 告知客户端结果。CONNECT 包含固定包头、可变包头、消息载荷。CONNACK 包含固定包头、可变包头。
CONNECT 报文格式介绍
可变包头由如下构成协议名(Protocol Name)、协议等级(Protocol Level)、连接标志(Connect Flags)、保持连接(Keep Alive)
协议名:一般 3.1.1 以后都用 MQTT
协议等级:4 代表 3.1.1,3 代表 3.1.1 之前的版本,5 代表 5.0
保持连接时间:客户端传输完成一个控制报文的时刻,到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔
- 如果保持连接时间设置为
60
秒,那么每60秒客户端必须至少向服务器发送一次消息(或进行心跳检查),如果没有,则服务器可能会断开连接。
连接标志
连接标志,主要用于指示 payload(有效荷载) 域存在哪些内容
Clean Session:标识客户端是(0)否(1)建立一个持久化的会话,当 Clean Session 的标识设为 0 时,代表客户端希望建立一个持久会话的连接,代理服务器将存储该客户端订阅的主题和未接受的消息,否则(设置为1)代理服务器不会存储这些数据,同时在建立连接时清除这个客户端之前存在的持久化会话所保存的数据。
如果连接标志包含所有内容,必须按如下这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密 码,阿里云 IOT 不支持 will,因此 payload(有效荷载) 简化成如下:
所谓遗嘱功能,就是当服务器检测到客户端非正常断开连接时,就会向客户端遗嘱主题中发送相应的遗嘱消息;上图中就是不使用遗嘱功能的 payload 图。
CONNACK 报文格式介绍
可变包头由如下构成:连接确认标志,返回码
当 CONNECT 报文中的 Clean Session 标志设置为 1 时,当前会话标志为。
CONNACK 没有 payload
PUBLISH(发布)与SUBSCIRBE(订阅)报文
发布者(Publisher)
负责将消息发布到主题上,发布者一次只能向一个主题发送数据,发布者发布消息时也无需关心订阅者是否在线。
订阅者(Subscriber)
订阅者通过订阅主题接收消息,且可一次订阅多个主题。MQTT 还支持通过共享订阅的方式在多个订阅者之间实现订阅的负载均衡。
代理(Broker)
负责接收发布者的消息,并将消息转发至符合条件的订阅者。另外,代理也需要负责处理客户端发起的连接、断开连接、订阅、取消订阅等请求。
主题(Topic)
主题是 MQTT 进行消息路由的基础,它类似 URL 路径,使用斜杠 / 进行分层,比如 sensor/1/temperature。一个主题可以有多个订阅者,代理会将该主题下的消息转发给所有订阅者;一个主题也可以有多个发布者,代理将按照消息到达的顺序转发。
MQTT 发布与订阅流程
MQTT 是基于主题来进行消息流向的;
topic(主题):topic 在 MQTT 里面是消息传递的基础,代表了消息的流向,从本质上看,topic 是一串字符串,可以使用正斜杠对topic 进行分级。
例子:比如我向 A 代理服务器订阅了如下主题消息”/编程知识/嵌入式/C语言”,当有其他人向 A 服务器推送了主题“/编程知识/嵌入式/C语言”的消息时候,那么代理服务器就会把这条消息推送给我;如果别人推送的是“/编程知识/嵌入式/C++”,由于主题不匹配,那么代理服务器就不会把这条消息推送给我们。
订阅协议格式
图片展示了与订阅和响应相关的数据格式,主要分为两部分:订阅请求报文和订阅响应报文。
固定报头:
- 包含高 4 位的订阅报文类型(byte1),以及底 4 位的固定QOS(Quality of Service)值(byte1)。
- 从第二字节开始,表示后续可变报头和消息负载的总长度。
可变报头:
- PacketID:标识报文的唯一ID。PacketID 的作用是为报文的发送和接收提供唯一性,确保发送方和接收方在处理时能够正确地匹配请求和响应报文;也就是说发送与回应中的 PacketID 要保持一致。
帧载荷:
2 byte 主题长度,表示主题的字节数。
主题内容(byte3到byteN),即具体的订阅内容。
服务质量要求QoS:比如有个客户端要向这个主题发布一条消息,那么客户端发布报文的 QoS 等级就不能高于此处的 QoS 等级。
发布协议格式
实现ESP32连接MQTT
MQTTX客户端工具
这里要使用一个 MQTTX 这个工具。
我们要利用这个工具去连接到一个免费的给我们测试用的 MQTT 服务器中,操作如下:
点击连接后就可以了。
ESP32实现
实现分为两个部分,首先要让 ESP32 连接到互联网,之后才可以进行 MQTT 操作
1 | // sta.c |
运行后即可通过 MQTTX 实现与 EPS32 之间的数据交互,如图所示:
问题
一、导入的头文件报错
ctrl + shift + P 选择:ESP-IDF: Add vscode configuration Folder
即可加入 IDF 路径
二、新建的多个文件报错
Build 下就好了