MQTT 协议:物联网首选轻量通信

FreeGuideOnline 最新 2026-07-03

home/livingroom/temperature building/floor2/room205/light/status


- **通配符支持**:订阅者可以使用通配符一次订阅多个主题。
  - **单层通配符 `+`**:匹配任意一个完整层级。`home/+/temperature` 会匹配 `home/livingroom/temperature`、`home/bedroom/temperature`,但不匹配 `home/livingroom/humidity`。
  - **多层通配符 `#`**:匹配当前层及之后的所有层级,只能放在主题末尾。`home/#` 匹配以 `home/` 为前缀的所有主题,如 `home/livingroom/temperature`、`home/bedroom/light` 等。
- **以`$`开头的主题**:通常为系统内部使用,客户端不能直接向这类主题发布消息,且订阅 `+` 和 `#` 不会匹配到它们。

### 服务质量(QoS)

MQTT定义了三种消息发布服务质量等级,用于平衡可靠性和性能:

- **QoS 0:至多一次**  
  消息发送完全依赖底层TCP/IP网络,不进行确认和重试。  
  接收方可能收到一次消息,也可能根本收不到。适用于传感器定时上报非关键数据,允许偶尔丢失。

- **QoS 1:至少一次**  
  确保消息至少被送达一次,可能会重复。发送方在发出消息后要求接收方回复PUBACK报文,若未收到确认则重发。  
  接收方可能会收到多条相同的消息,因此需要业务逻辑进行去重处理。这是MOST常用等级。

- **QoS 2:恰好一次**  
  通过两次握手保证消息有且仅被送达一次,无丢失无重复。流程用PUBLISH、PUBREC、PUBREL、PUBCOMP四步交互完成。  
  开销最大,适用于金融交易、控制指令等不能容忍丢失或重复的场景。

> 注意:QoS是发布者和订阅者分别与Broker之间达成的约定,而非端到端的保证。例如,发布者以QoS 1发给Broker,Broker以QoS 0转发给订阅者,那么订阅者可能收不到消息。

## MQTT控制报文详解

MQTT协议共定义了15种控制报文类型,每个报文由固定头部、可变头部和有效载荷组成。以下解释最常用的几种。

### CONNECT与CONNACK

- **CONNECT**:客户端发送给Broker的连接请求,包含协议名、协议版本、清理会话标志(Clean Session)、保活时间(Keep Alive)、客户端ID以及可选的用户名/密码和遗嘱消息等。
- **CONNACK**:Broker对CONNECT的确认,包含会话存在标志和返回码(如连接接受、协议版本错误、标识符被拒、服务不可用等)。

保活时间用于在没有消息交互时,客户端定期发送PINGREQ以维持连接,若Broker在1.5倍保活时间内未收到任何报文,将断开连接。

### PUBLISH与PUBACK

- **PUBLISH**:发布消息,包含主题名、报文ID(QoS>0时需要)、有效载荷和QoS等级等字段。
- **PUBACK**:对QoS 1发布消息的确认。Broker收到QoS 1的PUBLISH后存储消息并向订阅者转发,同时回复发送者PUBACK。对QoS 2的流程更复杂,但由PUBREC/PUBREL/PUBCOMP完成,详见协议规范。

### SUBSCRIBE与SUBACK

- **SUBSCRIBE**:客户端发送订阅请求,可一次订阅多个主题,并为每个主题指定要求的QoS。报文ID必须存在。
- **SUBACK**:Broker返回订阅结果,为每个订阅主题单独返回一个授予的QoS(可能低于请求的QoS)。

订阅者指定的QoS是期望Broker在向自己转发消息时使用的最大QoS级别。实际授予的QoS由Broker根据主题自身限制决定。

### PINGREQ与PINGRESP

- **PINGREQ**:客户端在无数据交互时发送心跳请求,维持连接活性。
- **PINGRESP**:Broker响应PINGREQ,表明连接正常。

### DISCONNECT

- **DISCONNECT**:客户端在正常关闭连接前发送,告知Broker自己将断开。若设置了遗嘱消息,则此时不会发布遗嘱,因为这是正常断开。

## MQTT 5.0 新特性概览

MQTT 5.0在保持轻量核心的基础上引入了大量现代功能,显著提升了协议的可扩展性和适用场景:

- **会话与消息过期**:可以为会话和单条消息设置过期时间,超时后将自动清除,避免长期占用存储。
- **原因码与用户属性**:所有确认报文均能携带原因码字符串和用户自定义键值对,便于错误诊断和元数据传递。
- **共享订阅**:允许多个订阅者组共享同一订阅,消息将在组内负载均衡分发,用于构建消费者组。
- **请求/响应模式**:通过响应主题(Response Topic)和关联数据(Correlation Data)原生支持请求-响应,无需额外协议。
- **更强的认证与授权**:支持增强认证方法(如SCRAM),在连接阶段可执行多步安全挑战。
- **Topic Alias**:将主题名映射为整数别名,减少重复传输长主题名的开销。
- **Flow Control**:收发双方可限制未确认PUBLISH报文的数量,实现流量控制。

对于新项目,建议直接采用MQTT 5.0;如果项目需要与大量旧有设备兼容,MQTT 3.1.1仍是可靠选择。

## 动手实践:搭建你的第一个MQTT通信

### 使用公共代理进行测试

在开始编写代码前,你可以使用公开免费的MQTT代理来快速体验。例如:

- **Broker地址**:`broker.emqx.io`  
- **TCP端口**:1883(非加密)  
- **WebSocket端口**:8083

你可以使用图形化工具[MQTTX](https://mqttx.app/)或命令行工具`mosquitto_pub`/`mosquitto_sub`来发布和订阅消息。

**简单命令行示例(需安装mosquitto客户端):**
```bash
# 终端1:订阅主题 tutorial/test,打印接收到的消息
mosquitto_sub -h broker.emqx.io -t "tutorial/test" -q 1

# 终端2:发布一条消息到同一主题
mosquitto_pub -h broker.emqx.io -t "tutorial/test" -m "Hello MQTT" -q 1

你会看到终端1立即打印出“Hello MQTT”。

Python代码示例

使用paho-mqtt库,可以轻松实现发布和订阅功能。

订阅端 client_sub.py

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))
    client.subscribe("tutorial/test", qos=1)

def on_message(client, userdata, msg):
    print(f"Received '{msg.payload.decode()}' on topic '{msg.topic}'")

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

client.connect("broker.emqx.io", 1883, 60)
client.loop_forever()

发布端 client_pub.py

import paho.mqtt.client as mqtt
import time

client = mqtt.Client()
client.connect("broker.emqx.io", 1883, 60)

# 发布消息,QoS 1
client.publish("tutorial/test", payload="Hello from Python", qos=1)
time.sleep(1)  # 等待网络发送完成
client.disconnect()