是什么

MQTT(Message Queuing Telemetry Transport)是一种基于发布/订阅模式的轻量级通信协议,专为低带宽、高延迟或不稳定网络环境设计。由IBM于1999年开发,现已成为物联网(IoT)领域最主流的协议之一。其核心思想是设备通过代理服务器(Broker)进行消息传递,支持一对多通信,适用于传感器数据上报、远程控制等场景。MQTT协议基于TCP/IP,默认端口为1883(TLS加密为8883),最新版本为MQTT 5.0(兼容MQTT 3.1.1)。


核心特性

  1. 轻量高效
    • 最小报文仅2字节,适合资源受限设备(如MCU)。
  2. 灵活的发布/订阅模型
    • 支持主题通配符(+单层、#多层),例如:sensor/+/temperature
  3. 多级QoS保障
    • 根据场景选择消息可靠性(如QoS 0用于日志上报,QoS 2用于支付指令)。
  4. 遗言消息(Last Will)
    • 设备异常离线时,Broker自动发布预设消息,便于状态监控。
  5. 会话持久化
    • Broker可保存未确认消息,设备重连后恢复通信。

概念及特性扫盲

1. Broker和Client

如下图所示就是MQTT的大致通信过程。其中Broker是一个后台服务,供其它客户端连接,Broker可以比喻成快递站,Client表示是一个客户端,Client可以同时订阅消息和发布消息,Client可以比喻成人。所以mqtt的通信过程就是有一个Broker作为中心,有很多Client连接到Broker,大家通过Broker来实现消息的发送和接收。Broker有现成的,不需要自己开发,轻量级的有mosquitto,大的有EMQX。我们开发要做的就是如何开发客户端,利用mqtt通信,使得客户端之间实现交互。

PUBLISH/SUBSCRIBE
PUBLISH/SUBSCRIB
PUBLISH/SUBSCRIB
Client1
Broker
Client2
Client3

2. 发布(pub)、订阅(sub)和主题(topic)

mqtt的发布和订阅机制很好理解,举个栗子: broker是微信后台,client是微信用户,topic是微信群聊,pub是在群里发消息,sub是在群里收到消息。 所以就是客户端可以订阅自己感兴趣topic(自己定义),当有其它客户端往这个topic发送消息的时候,我就收到这个消息,没有订阅的话就不会收到这条消息。

3. 消息质量(QoS)

pub发布的消息有三种消息质量可以选择。

  • QoS 0:订阅者最多收到一次
  • QoS 1:订阅者最少收到一次
  • QoS 2:订阅者只会收到一次
3.1 QoS 0(最多一次)

**为什么 QoS 0 消息会丢失?**当我们使用 QoS 0 传递消息时,消息的可靠性完全依赖于底层的 TCP 协议。而 TCP 只能保证在连接稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭、重置,仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息。这也是 QoS 0 消息最主要的丢失场景。

Publisher Broker Subscriber PUBLISH (QoS=0) PUBLISH (QoS=0) 无确认,可能丢失 Publisher Broker Subscriber
3.2 QoS 1(最少一次)

为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才能认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。QoS 1 需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与 PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确的 PUBLISH 报文缓存。

Publisher Broker Subscriber PUBLISH (QoS=1, PID=123) PUBACK (PID=123) PUBLISH (QoS=1, PID=456) PUBACK (PID=456) 超时未收到ACK则重发 Publisher Broker Subscriber
3.3 QoS 2(只有一次)

QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流程。

Publisher Broker Subscriber PUBLISH(QoS=2, PID=100) PUBREC(PID=100) PUBREL(PID=100) PUBCOMP(PID=100) PUBLISH(QoS=2, PID=200) PUBREC(PID=200) PUBREL(PID=200) PUBCOMP(PID=200) Publisher Broker Subscriber
  1. 首先,发送方存储并发送 QoS 为 2 的 PUBLISH 报文以启动一次 QoS 2 消息的传输,然后等待接收方回复 PUBREC 报文。这一部分与 QoS 1 基本一致,只是响应报文从 PUBACK 变成了 PUBREC。
  2. 当发送方收到 PUBREC 报文,即可确认对端已经收到了 PUBLISH 报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地存储的 PUBLISH 报文,然后发送一个 PUBREL 报文,通知对端自己准备将本次使用的 Packet ID 标记为可用了。与 PUBLISH 报文一样,我们需要确保 PUBREL 报文到达对端,所以也需要一个响应报文,并且这个 PUBREL 报文需要被存储下来以便后续重传。
  3. 当接收方收到 PUBREL 报文,也可以确认在这一次的传输流程中不会再有重传的 PUBLISH 报文到达,因此回复 PUBCOMP 报文表示自己也准备好将当前的 Packet ID 用于新的消息了。
  4. 当发送方收到 PUBCOMP 报文,这一次的 QoS 2 消息传输就算正式完成了。在这之后,发送方可以再次使用当前的 Packet ID 发送新的消息,而接收方再次收到使用这个 Packet ID 的 PUBLISH 报文时,也会将它视为一个全新的消息。

4. retain消息机制和遗嘱消息(last will)

4.1 retain消息机制

当我们使用MQTT客户端发布消息(PUBLISH)时,如果将RETAIN标志位设置为true,那么MQTT服务器会将最近收到的一条RETAIN标志位为true的消息保存在服务器端(内存或文件)。

PUBLISH Retain=1
SUBSCRIBE
立即发送Retain消息
Publisher
Broker
存储最新消息
新Subscriber

特别注意:MQTT服务器只会为每一个Topic保存最近收到的一条RETAIN标志位为true的消息!也就是说,如果MQTT服务器上已经为某个Topic保存了一条Retained消息,当客户端再次发布一条新的Retained消息,那么服务器上原来的那条消息会被覆盖!每当MQTT客户端连接到MQTT服务器并订阅了某个topic,如果该topic下有Retained消息,那么MQTT服务器会立即向客户端推送该条Retained消息

4.2 遗嘱消息(last will)

遗愿消息实际是一个发布的消息,只是这条消息比较特殊,它是在客户端连接时设置的一条默认消息(自定义的topic+消息内容),服务器会存储这一条消息,当客户端异常断开时,服务器会将这条消息发布出来。所以它叫遗嘱消息。

Client Broker Subscriber CONNECT (Will Topic=alarm, Will QoS=1) CONNACK 正常断开 ->> 无遗嘱 异常断开(如断网) PUBLISH (Topic=alarm, QoS=1) Client Broker Subscriber
4.3 主题通配符

单层通配符(+)

加号 (“+”) 是用于单个主题层级匹配的通配符。在使用单层通配符时,单层通配符必须占据整个层级,例如:

+ 有效
sensor/+ 有效
sensor/+/temperature 有效
sensor+ 无效(没有占据整个层级)

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

sensor/1/temperature
sensor/2/temperature
...
sensor/n/temperature

但是不会匹配以下主题:

sensor/temperature
sensor/bedroom/1/temperature

多层通配符(#)

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

# 有效,匹配所有主题
sensor/# 有效
sensor/bedroom# 无效(没有占据整个层级)
sensor/#/temperature 无效(不是主题最后一个字符)

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

sensor
sensor/temperature
sensor/1/temperature

怎么用

broker安装

可以选择安装mosquitto, 各个系统的都能安装,很简单,找到一篇windows下的安装部署博客

工具推荐

如果安装了mosquitto,可以使用它自带的程序,命令行mosquitto_sub和mosquitto_pub 实现订阅和发布。工具的话可以用mqttfx或者MQTTX

C/C++

可以使用libmosquitto。懒得写了,找了一个demo

python

python可以使用paho,这是官方demo


感想

mqtt的使用不难,重点在于如何利用mqtt的特性去设计协议从而实现需求功能。后面我会从自己的项目中去讲解如何基于mqtt设计业务协议。


关于我

抖音:嵌入式石小星

CSDN:嵌入式石小星

微信公众号:嵌入式石小星

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐