收不到MQTT消息主因是会话配置、协议匹配或Broker行为不一致;需设clean:false、用WSS协议、连接成功后再订阅、全局单例管理客户端并正确处理生命周期。

收不到消息,八成不是代码写错了,而是会话配置、协议匹配或Broker行为没对上。
cleanSession 设为 false 才能收到离线消息
默认 cleanSession: true 时,每次重连都算“新客户端”,Broker 会丢掉之前所有订阅和未投递的 QoS1/QoS2 消息。哪怕你用的是同一个 clientId,也收不到断线期间发来的消息。
必须显式设为 false:
const client = mqtt.connect('wss://broker.example.com', {
clientId: 'uniapp-user-123',
clean: false, // 注意:mqtt.js v4+ 用 clean,v3 用 cleanSession
reconnectPeriod: 1000,
keepalive: 60
});
- 这个参数必须和服务端(如 EMQX、HiveMQ)的会话保留策略一致
- iOS 真机下如果仍收不到,检查 Broker 是否启用了
session_expiry_interval且值太小(比如 0 或 30 秒) - 改完后首次连接会触发“会话恢复”,
on('connect')回调里packet.sessionPresent为true表示恢复成功
确认协议和端口在 APP 端真正可用
uni-app 的 APP 端不支持裸 TCP,只能走 WebSocket 协议(ws 或 wss),而且 iOS 对证书校验极严——用自签名证书或 Let’s Encrypt 通配符证书都可能失败,报错类似 -9807 或 -9824。
- 开发阶段优先用公开可验证的 WSS 地址,例如
wss://broker.hivemq.com:8883/mqtt(注意路径带/mqtt) - Android 能连上
ws不代表 iOS 能连上wss;反过来,iOS 连得上不代表 Android 就不会报Protocol not supported(常见于 HBuilderX 云打包未勾选“WebSocket 支持”) - HBuilderX 项目设置里必须开启:「App模块配置」→ 勾选「WebSocket」;iOS 还要加
NSAppTransportSecurity白名单(仅调试期临时允许)
订阅必须等 connect 成功后再执行
很多人把 client.subscribe() 写在 connect 事件外,或者放在 on('connect') 外层,结果连接还没建立就发了 subscribe 请求,MQTT.js 会静默丢弃。
正确顺序只有一种:
client.on('connect', () => {
console.log('MQTT connected');
client.subscribe('device/status/+', { qos: 1 }, (err) => {
if (err) console.error('subscribe failed:', err);
});
});
- 不要在
on('reconnect')里重复 subscribe ——clean: false下订阅关系已由 Broker 持久化,重连后自动恢复 - 如果主题含通配符(如
+或#),确保 Broker 允许该模式(EMQX 默认允许,有些私有 Broker 需手动开启allow_anonymous或 ACL 规则) - 用 MQTT 客户端工具(如 MQTTX)向同一主题发消息,确认 Broker 确实收到了——排除服务端路由问题
消息回调被覆盖或未绑定到正确实例
uni-app 页面频繁切换、组件销毁重建时,容易反复 new MQTT 实例但没 client.end(),旧实例的 on('message') 还挂着,新实例又绑了一次,导致回调执行两次或完全不执行。
- 全局只维护一个 client 实例,挂到
uni.$mqtt或 Pinia store 里,不要在页面onLoad里 new - 务必在页面卸载前调用
client.unsubscribe()+client.end(false)(false 表示不发 DISCONNECT 包,避免 Broker 清除会话) - 监听 message 时别漏掉 topic 参数:
client.on('message', (topic, payload) => { ... }),payload 是Uint8Array,需转字符串:new TextDecoder().decode(payload)
最常被忽略的一点:Broker 的 QoS 级别和客户端 publish 的 QoS 不匹配时,消息可能被静默丢弃。比如你用 QoS 0 发布,但订阅时指定了 QoS 2,部分 Broker(尤其是旧版 EMQX)会拒绝投递。统一用 QoS 1 是最稳妥的折中选择。


















