嵌入式软件工程师养成记-必备通信协议篇之mqtt通信
是什么
MQTT(Message Queuing Telemetry Transport)是一种基于发布/订阅模式的轻量级通信协议,专为低带宽、高延迟或不稳定网络环境设计。由IBM于1999年开发,现已成为物联网(IoT)领域最主流的协议之一。其核心思想是设备通过代理服务器(Broker)进行消息传递,支持一对多通信,适用于传感器数据上报、远程控制等场景。MQTT协议基于TCP/IP,默认端口为1883(TLS加密为8883),最新版本为MQTT 5.0(兼容MQTT 3.1.1)。
核心特性
- 轻量高效
- 最小报文仅2字节,适合资源受限设备(如MCU)。
- 灵活的发布/订阅模型
- 支持主题通配符(
+单层、#多层),例如:sensor/+/temperature。
- 支持主题通配符(
- 多级QoS保障
- 根据场景选择消息可靠性(如QoS 0用于日志上报,QoS 2用于支付指令)。
- 遗言消息(Last Will)
- 设备异常离线时,Broker自动发布预设消息,便于状态监控。
- 会话持久化
- Broker可保存未确认消息,设备重连后恢复通信。
概念及特性扫盲
1. Broker和Client
如下图所示就是MQTT的大致通信过程。其中Broker是一个后台服务,供其它客户端连接,Broker可以比喻成快递站,Client表示是一个客户端,Client可以同时订阅消息和发布消息,Client可以比喻成人。所以mqtt的通信过程就是有一个Broker作为中心,有很多Client连接到Broker,大家通过Broker来实现消息的发送和接收。Broker有现成的,不需要自己开发,轻量级的有mosquitto,大的有EMQX。我们开发要做的就是如何开发客户端,利用mqtt通信,使得客户端之间实现交互。
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 消息最主要的丢失场景。
3.2 QoS 1(最少一次)
为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才能认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。QoS 1 需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与 PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确的 PUBLISH 报文缓存。
3.3 QoS 2(只有一次)
QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流程。
- 首先,发送方存储并发送 QoS 为 2 的 PUBLISH 报文以启动一次 QoS 2 消息的传输,然后等待接收方回复 PUBREC 报文。这一部分与 QoS 1 基本一致,只是响应报文从 PUBACK 变成了 PUBREC。
- 当发送方收到 PUBREC 报文,即可确认对端已经收到了 PUBLISH 报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地存储的 PUBLISH 报文,然后发送一个 PUBREL 报文,通知对端自己准备将本次使用的 Packet ID 标记为可用了。与 PUBLISH 报文一样,我们需要确保 PUBREL 报文到达对端,所以也需要一个响应报文,并且这个 PUBREL 报文需要被存储下来以便后续重传。
- 当接收方收到 PUBREL 报文,也可以确认在这一次的传输流程中不会再有重传的 PUBLISH 报文到达,因此回复 PUBCOMP 报文表示自己也准备好将当前的 Packet ID 用于新的消息了。
- 当发送方收到 PUBCOMP 报文,这一次的 QoS 2 消息传输就算正式完成了。在这之后,发送方可以再次使用当前的 Packet ID 发送新的消息,而接收方再次收到使用这个 Packet ID 的 PUBLISH 报文时,也会将它视为一个全新的消息。
4. retain消息机制和遗嘱消息(last will)
4.1 retain消息机制
当我们使用MQTT客户端发布消息(PUBLISH)时,如果将RETAIN标志位设置为true,那么MQTT服务器会将最近收到的一条RETAIN标志位为true的消息保存在服务器端(内存或文件)。
特别注意:MQTT服务器只会为每一个Topic保存最近收到的一条RETAIN标志位为true的消息!也就是说,如果MQTT服务器上已经为某个Topic保存了一条Retained消息,当客户端再次发布一条新的Retained消息,那么服务器上原来的那条消息会被覆盖!每当MQTT客户端连接到MQTT服务器并订阅了某个topic,如果该topic下有Retained消息,那么MQTT服务器会立即向客户端推送该条Retained消息
4.2 遗嘱消息(last will)
遗愿消息实际是一个发布的消息,只是这条消息比较特殊,它是在客户端连接时设置的一条默认消息(自定义的topic+消息内容),服务器会存储这一条消息,当客户端异常断开时,服务器会将这条消息发布出来。所以它叫遗嘱消息。
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
感想
mqtt的使用不难,重点在于如何利用mqtt的特性去设计协议从而实现需求功能。后面我会从自己的项目中去讲解如何基于mqtt设计业务协议。
关于我
抖音:嵌入式石小星
CSDN:嵌入式石小星
微信公众号:嵌入式石小星
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)