MQTT 实战与排错 — 抓包、mosquitto 命令与故障排查

MQTT 实战:Wireshark 抓包过滤式、mosquitto_pub/sub 实操、Broker 配置、常见故障与排错、与 CoAP/AMQP 对比、速查表与面试题 / 应用层(物联网)/ TCP 1883、8883 / OASIS MQTT v3.1.1、v5.0

这篇你能学到

  • 怎么用 Wireshark mqtt 过滤式抓包看 CONNECT/CONNACK 返回码、Topic、QoS、Retain 位,以及 mosquitto_pub/sub 把发布/订阅跑通。
  • 怎么配 Mosquitto/EMQX Broker(端口、TLS、密码文件、ACL、桥接),以及常见客户端库与桥接用法。
  • 遇到连接被拒、收不到消息、遗嘱不触发、频繁掉线、TLS 失败、消息丢失等 9 类问题时先记结论再按"网络→协议→应用→Broker"四层排查,以及与 CoAP/AMQP/HTTP 的选型对比。

抓包观察(Wireshark 过滤式)

MQTT 走 TCP,Wireshark 用 mqtt 显示过滤器解析;若走 WebSocket 则用 mqtt 仍可读(Wireshark 会解 WebSocket 内的 MQTT 帧)。下面这张表是抓包时的"检索目录":

目的Wireshark / tshark 过滤式
所有 MQTT 流量mqtt
指定 TCP 端口tcp.port == 1883tcp.port == 8883
仅 CONNECT / CONNACK`mqtt.msgtype == 1
仅 PUBLISHmqtt.msgtype == 3
指定主题mqtt.topic contains "factory/line1"
指定 QoSmqtt.qos == 2
保留消息mqtt.retain == 1
遗嘱相关mqtt.willflag == 1(CONNECT 中)
连接用户名mqtt.username / mqtt.password(明文凭据,注意安全)
协议版本在 CONNECT 可变头看 Protocol Level(4=v3.1.1, 5=v5.0)
抓包命令(tshark)tshark -i eth0 -Y "mqtt" -T fields -e mqtt.topic -e mqtt.qos
命令行抓包tcpdump -i eth0 -w mqtt.pcap 'tcp port 1883' 后用 Wireshark 打开

提示:8883(TLS)下 Wireshark 默认看不到明文 MQTT,需在 编辑 → 首选项 → Protocols → TLS 配置 (Pre)-Master-Secret 日志文件 或导入私钥解密后才能解析 MQTT 层。

mosquitto 自带客户端快速验证:下面 4 条覆盖"订阅、发保留消息、带凭据+遗嘱、看系统主题"四个最常见动作,用来确认 Broker 是否正常工作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 订阅主题(前台阻塞,收到即打印)
mosquitto_sub -h broker.example.com -p 1883 -t "factory/+/temp" -v

# 发布一条 QoS 1 的保留消息
mosquitto_pub -h broker.example.com -p 1883 -t "factory/line1/temp" \
  -m "23.5" -q 1 -r

# 带用户名密码 + 遗嘱(异常退出时 Broker 代发 offline)
mosquitto_pub -h broker.example.com -p 1883 -t "status/dev1" -m "online" \
  -u sensor1 -P secret \
  --will-topic "status/dev1" --will-payload "offline" --will-qos 1 --will-retain

# 订阅 $SYS 系统主题查看 Broker 负载
mosquitto_sub -h localhost -t '$SYS/broker/load/bytes/received/1min' -v

常用命令 / 配置

1. Eclipse Mosquitto Broker 配置(mosquitto.conf

下面这份配置同时做了"禁匿名 + 开 TLS + 配密码文件 + 持久化 + ACL"五件事,是生产可用的最小骨架:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 监听端口(1883 明文 + 8883 TLS)
listener 1883
allow_anonymous false # 禁止匿名,强制认证

listener 8883
cafile /etc/mosquitto/ca.crt
certfile /etc/mosquitto/server.crt
keyfile /etc/mosquitto/server.key
require_certificate false # 单向 TLS;true 为双向(mTLS)

# 密码文件(mosquitto_passwd -c /etc/mosquitto/passwd sensor1)
password_file /etc/mosquitto/passwd

# 持久化会话与保留消息
persistence true
persistence_location /var/lib/mosquitto/

# 访问控制(ACL):用户 sensor1 只能读写自己设备主题
acl_file /etc/mosquitto/acl.conf

acl.conf 示例:

1
2
3
user sensor1
topic readwrite status/dev1
topic readwrite factory/line1/#

2. EMQX(云原生 Broker,常用命令)

1
2
3
4
5
6
7
emqx start # 启动
emqx_ctl status # 查看状态
emqx_ctl clients list # 在线客户端
emqx_ctl subscriptions list # 订阅关系
emqx_ctl brokers ping # 集群节点
# Dashboard: http://localhost:18083 (默认 admin/public)
# 规则引擎可将 topic 桥接到 Kafka / MySQL / InfluxDB

3. 客户端库(开发常用)

语言
Pythonpaho-mqttimport paho.mqtt.client as mqtt
Goeclipse/paho.mqtt.golang
Node.jsmqtt(npm 包)
C/C++paho.mqtt.c
JavaEclipse Paho Java / HiveMQ MQTT Client
嵌入式esp-mqtt(ESP32)、nanomq(边缘 MQTT-SN 桥接)

4. 桥接(Broker ↔ Broker)

下面这段把本地 factory/# 转发到云端 Broker,是"边缘↔云"打通的标准做法:

1
2
3
4
5
6
# mosquitto.conf 将本地 topic 桥接到远端 Broker
connection bridge-to-cloud
address cloud.example.com:8883
topic factory/# out 1 cloud/factory/ # 本地 factory/# 转发到云端 cloud/factory/#
remote_username relay
remote_password ******

常见故障与排错

故障 1:连接被拒(CONNACK 返回码非 0)

先记住结论:连接被拒先看 CONNACK 的返回码/原因码,几乎都指向"版本不对、ClientID 冲突、凭据错、未授权"四选一。v3.1.1 返回码:1=协议版本不支持、2=客户端 ID 不合格、3=服务器不可用、4=用户名/密码错、5=未授权。

可能原因排查与处置
v3.1.1 返回码:1=协议版本不支持、2=客户端 ID 不合格、3=服务器不可用、4=用户名/密码错、5=未授权看 CONNACK 返回码;v5 看原因码;核对 Protocol Level(4=v3.1.1,5=v5)、ClientID 唯一性、凭据、ACL

故障 2:订阅收不到消息

先记住结论:收不到消息先怀疑"通配符层数写错"或"发布/订阅 QoS 取小后没达到预期",再查 Retained 是否漏设导致晚订阅者错过。Wireshark 抓 mqtt.topic 一比对就知道。

可能原因排查与处置
主题通配符写错(+/# 层数不符)、发布与订阅 QoS 取小后低于预期、Retained 没设导致晚订阅者错过Wireshark 抓 mqtt.topic 比对;确认订阅端 SUBACK 授予 QoS;用 mosquitto_sub -v 验证;关键状态用 RETAIN=1

故障 3:QoS 2 消息重复/乱序

先记住结论:QoS2 的"去重"靠报文标识符(PacketId),重复/乱序通常是客户端没按 PacketId 维护未完成表——Broker 端一般已处理好,重点查客户端实现。

可能原因排查与处置
网络重传导致 PUBREC/PUBREL 重发;客户端未正确按 Packet Identifier 去重确保客户端按 PacketId 维护"未完成 QoS2"表;Broker 端通常已处理,重点查客户端实现

故障 4:遗嘱不触发

先记住结论:遗嘱只在"异常断线/心跳超时"时触发,优雅 DISCONNECT 不会发——调试必须拔网线/断电,别指望正常退出能看到;另外确认遗嘱 Topic 被 ACL 放行。

可能原因排查与处置
客户端是正常 DISCONNECT 断开(遗嘱不会发);或 Broker 在 Keep Alive 超时前没检测到断线异常断网(拔网线/断电)才触发;确认 Keep Alive 合理;遗嘱 Topic 是否被 ACL 允许发布

故障 5:频繁掉线 / 假死

先记住结论:频繁掉线多半是 Keep Alive 太大发现不了断网,或 NAT/防火墙把空闲长连接回收了——缩短 Keep Alive、定时 PINGREQ、防火墙侧开长连接保活。

可能原因排查与处置
Keep Alive 过大导致断网很久才被发现;NAT/防火墙空闲连接被回收(如 5 分钟无包即踢)缩短 Keep Alive(如 30~60s);客户端定时 PINGREQ;防火墙侧设长连接保活;移动网络建议更小 Keep Alive

故障 6:TLS 连接失败

先记住结论:8883 握手失败先验证书链——SNI/域名不符、过期、CA 没下发客户端、单向/双向配置不一致,是四类最常见原因。

可能原因排查与处置
证书链不完整、SNI/域名不符、证书过期、8883 端口被中间盒拦截openssl s_client -connect host:8883 验证证书;检查 require_certificate 双向/单向配置;确认 CA 已下发客户端

故障 7:Broker CPU/内存飙升

先记住结论:Broker 资源飙升通常是 $SYS 轮询过多、巨型 Retained、订阅膨胀、Receive Maximum 没设导致窗口打满——限 Receive Maximum、清 stale 会话、监控 $SYS

可能原因排查与处置
大量 $SYS 轮询、巨型 Retained、订阅膨胀、未设 Receive Maximum 导致窗口打满限制 Receive Maximum(v5)、定期清理 stale 会话、监控 $SYS/broker/...;EMQX 用规则引擎削峰

故障 8:消息丢失(以为 QoS 足够)

先记住结论:“感觉 QoS 够却丢消息"几乎都是"发布用了 QoS0"或"桥接转发时被降级”——关键数据发布必须 QoS1/2,桥接两端 QoS 要协商一致。

可能原因排查与处置
发布用 QoS0 却期望不丢;或 Bridge 转发时降级关键数据发布用 QoS1/2;桥接两端协商一致 QoS;确认 topic ... out QoS 配置

故障 9:安全告警:明文 1883 暴露公网

先记住结论:明文 1883 出公网 = 凭据和消息裸奔,等于敞开大门——必须关匿名、上 TLS、配 ACL、限来源 IP,必要时加 JWT/认证插件。

可能原因排查与处置
未禁匿名、无 TLS、无 ACL关闭 allow_anonymous、启用 8883 TLS、配置 ACL、用安全组限制来源 IP;必要时加 mosquitto_auth_plugin/JWT

排错方法论(由下而上)

先记住结论:MQTT 排错按"网络 → 协议 → 应用 → Broker"四层从下往上查,绝大多数问题在前两层就能定位。

  1. 网络层tcpdump port 1883 确认 TCP 三次握手是否成功;telnet host 1883 验证端口可达。
  2. 协议层:Wireshark 看 CONNECT/CONNACK 返回码、SUBACK 授予 QoS、PUBLISH 的 Topic/QoS/Retain 位。
  3. 应用层:核对 ClientID 唯一、遗嘱/保留/会话语义是否符合预期、ACL 是否放行。
  4. Broker 侧:查 $SYS/broker/ 指标、日志(mosquitto -v 前台 verbose;EMQX emqx_ctl log)。

与其他协议对比

MQTT vs CoAP vs AMQP

维度MQTTCoAPAMQP
传输层TCP(可靠)UDP(可选 DTLS)TCP(可靠,复杂握手)
通信模型发布/订阅(Broker)请求/响应(REST 风格)队列 + 发布/订阅(Broker/Exchange)
头部开销极小(≥2B)极小(4B 固定头)较大(协议帧复杂)
适用网络弱网/移动/长连接极受限、无 TCP 的传感网企业内网、金融级可靠
QoS0/1/2 三级可确认/不可确认(轻)事务级(at-least/at-most/exactly)
典型实现Mosquitto/EMQX/HiveMQCalifornium/CoAP++RabbitMQ/ActiveMQ
一句话海量终端实时遥测类 HTTP 的受限节点企业消息总线

与 HTTP 的取舍

  • MQTT 优势:长连接低开销、服务端可主动推送(HTTP 需轮询或 WebSocket)、天然一对多广播、弱网友好。
  • HTTP 优势:穿透性好(80/443 通用)、生态成熟、无状态易水平扩展、适合偶发请求(如 OTA 固件下载)。实践中常"MQTT 做实时 + HTTP 做配置/升级"。

速查表

项目取值 / 说明
默认端口1883(明文)/ 8883(TLS)/ 80·443(WebSocket)
协议级别字节4 = v3.1.1;5 = v5.0
QoS 含义0 至多一次 / 1 至少一次 / 2 恰好一次
通配符+ 单层、# 多层(末尾)、$ 系统主题
控制报文数14 种(v3.1.1)+ AUTH(v5)= 15
最小报文固定头 2 字节(如 PINGREQ/PINGRESP)
剩余长度上限4 字节变长编码 → 268,435,455 B(≈256 MB)
心跳Keep Alive 秒;超时判定 1.5×KeepAlive
保留消息清除发 RETAIN=1 + 空载荷
常见 BrokerMosquitto、EMQX、HiveMQ、VerneMQ、RabbitMQ(插件)

常见面试题

  1. MQTT 为什么适合物联网? 头部小、长连接低功耗、发布订阅解耦多对多、弱网友好、QoS 可选。
  2. QoS 0/1/2 的区别? 至多一次 / 至少一次(PUBACK)/ 恰好一次(PUBREC+PUBREL+PUBCOMP 四次握手)。
  3. 发布与订阅 QoS 不一致怎么办? 取较小值;Broker 按订阅者请求的 QoS 转发。
  4. Retained 与 Last Will 的区别? Retained 是"主题最新值留存给新订阅者";Will 是"异常断线时代发的遗言"。
  5. Clean Session 的作用? 控制断线后订阅关系与离线消息是否保留(v5 由 Clean Start + Session Expiry 取代)。
  6. +# 的区别? + 匹配单层、# 匹配其后多层且必须末尾。
  7. 遗嘱什么时候触发、什么时候不触发? 异常断线/心跳超时触发;正常 DISCONNECT 不触发。
  8. MQTT 与 CoAP / AMQP 怎么选? TCP 长连接遥测选 MQTT;极受限 UDP 节点选 CoAP;企业级可靠消息总线选 AMQP。
  9. 8883 是什么、为什么需要 TLS? MQTT over SSL/TLS,防止凭据与消息被窃听/篡改,公网必开。
  10. MQTT 5.0 相比 3.1.1 主要增强? 原因码、Properties、Session Expiry、共享订阅、消息过期、主题别名、增强认证 AUTH、Response Topic 请求/响应模式。