📌 一、引言
MQTT(Message Queuing Telemetry Transport)是一种轻量级的机器对机器(M2M)通信协议,专为物联网和低带宽环境设计。其核心特点是采用发布/订阅模式,具有低功耗、高效性和高可靠性等优点,非常适合在资源有限的设备上运行。在微信小程序中集成 MQTT 可以实现实时数据传输等功能。本文将详细介绍如何在微信小程序中使用 MQTT,并分析其中的技术难点及注意事项。
🛠️ 二、实施步骤与关键代码解析
-
引入 MQTT 库 :微信小程序本身不直接提供 MQTT 客户端功能,因此需要借助第三方库来实现。目前常用的
mqtt.js版本有多个,如v4.1.0等。开发者可以根据项目需求选择合适的版本,并将其拷贝至项目根目录下新建的utils文件夹中,随后通过import mqtt from "../../utils/mqtt.min.js"方式引入mqtt.js v4.1.0 下载地址。 -
建立连接:微信小程序安全要求较高,与后台服务器之间的通讯必须使用 https/wss 协议。但由于部分用于测试的未备案域名无法完成常规配置,所以在微信开发工具中需勾选【不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书】选项。若已有备案过的自有 MQTT 服务器,则按要求完成相应配置即可(登录小程序后台》开发管理》服务器域名》socket合法域名)。以下是典型连接代码示例:
微信小程序中的 app.js
// app.js
import mqtt from "./utils/mqtt.min";
let connected = false;
// // 重连次数
// let reconnectAttempts = 0;
App({
async onLaunch() {},
onShow(options) {
connected = false;
this.globalData.mqttConnected = false;
},
onHide(options) {
this.disconnectMQTT();
},
initMqtt(cb) {
if (connected) {
cb && cb();
return;
}
// MQTT连接选项
const options = {
connectTimeout: 4000,
clientId: "wxapp_ca_" + Math.random().toString(16).substring(2, 10),
username: "实际用户名",
password: "实际用密码",
clean: true,
// 配置CA证书验证
ca: `-----BEGIN CERTIFICATE-----
MIIE/jCCA+agAwIBAgISBp77eeZKOPZRNgqf+FQ0YuXUMA0GCSqGSIb3DQEBCwUA
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
EwNSMTMwHhcNMjUwODIyMDE0ODU3WhcNMjUxMTIwMDE0ODU2WjAcMRowGAYDVQQD
ExF6aGN3LmJhbmtwZXRzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALzWv64Rm874UDTfvtuO4C0ny4WnmEVWeERzRfNNCJZRN/r9PizycLyJbUfG
1gUW6sCRT1Pzy72Q4dDt/i6k/bLQAE/0ilisc5Y4TQ3rQ5jGo3HVbk8HPfCHx57f
muHyQzcwofPyn8Zo+Zgvsg/hx8zTAOW9+tWsfAy+pxcF43aoSEvaxm6RCXYI80IO
MIIE/jCCA+agAwIBAgISBp77eeZKOPZRNgqf+FQ0YuXUMA0GCSqGSIb3DQEBCwUA
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
EwNSMTMwHhcNMjUwODIyMDE0ODU3WhcNMjUxMTIwMDE0ODU2WjAcMRowGAYDVQQD
ExF6aGN3LmJhbmtwZXRzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALzWv64Rm874UDTfvtuO4C0ny4WnmEVWeERzRfNNCJZRN/r9PizycLyJbUfG
1gUW6sCRT1Pzy72Q4dDt/i6k/bLQAE/0ilisc5Y4TQ3rQ5jGo3HVbk8HPfCHx57f
muHyQzcwofPyn8Zo+Zgvsg/hx8zTAOW9+tWsfAy+pxcF43aoSEvaxm6RCXYI80IO
r4b80pgPzoKTKFOewYzNVITS65kNdOjK
-----END CERTIFICATE-----`,
// 强制验证服务器证书,不需要证书可设为false,不传ca
rejectUnauthorized: true,
// 额外的TLS选项(可选)
secureProtocol: "",
};
// 连接地址(MQTT服务器WSS地址)
const connectUrl = "wxs://example.com:8083/mqtt";
// 建立连接
this.globalData.client = mqtt.connect(connectUrl, options);
// 监听连接成功
this.globalData.client.on("connect", () => {
console.log("MQTT 连接成功(已验证CA证书)");
connected = true;
this.globalData.mqttConnected = true;
cb && cb();
// 订阅主题
// this.subscribeTopic()
});
// // 监听接收消息
// this.globalData.client.on('message', (topic, message) => {
// const newMessage = {
// time: new Date().toLocaleString(),
// topic: topic,
// content: message.toString() && utils.jsonStrToJson(message.toString()),
// }
// console.log(1111, newMessage);
// this.globalData.messages = newMessage
// })
this.globalData.client.on("error", (err) => {
console.error("MQTT 错误:", err);
let errorMsg = "连接失败";
if (err.message.includes("certificate")) {
errorMsg = "证书验证失败";
} else if (err.message.includes("connection refused")) {
errorMsg = "连接被拒绝(检查账号密码)";
}
wx.showToast({
title: errorMsg,
icon: "none",
duration: 3000,
});
this.disconnectMQTT();
});
this.globalData.client.on("reconnect", () => {
// if (reconnectAttempts >= 5) {
// // 重连5次
try {
this.globalData.client.end();
connected = false;
this.globalData.mqttConnected = false;
console.log("重连失败");
this.initMqtt(cb);
} catch (error) {}
// return;
// }
// reconnectAttempts++;
console.log("正在重连...");
// wx.showToast({
// title: "正在重连...",
// icon: "loading",
// duration: 2000,
// });
});
this.globalData.client.on("close", () => {
console.log("连接已断开close");
connected = false;
this.globalData.mqttConnected = false;
});
},
// 订阅主题
subscribeTopic(topic, option = { qos: 0 }) {
try {
if (!connected) return;
this.globalData.client.subscribe(topic, option, (err) => {
if (!err) {
console.log("已订阅主题: ", topic);
} else {
console.error("订阅失败:", err);
// wx.showToast({
// title: "订阅失败: " + topic,
// icon: "none",
// duration: 2000,
// });
}
});
} catch (error) {}
},
// 取消订阅主题
unsubscribeTopic(topic) {
try {
if (!connected) return;
this.globalData.client.unsubscribe(topic, (err) => {
if (!err) {
console.log("取消订阅主题: ", topic);
} else {
console.error("取消订阅失败:", err);
wx.showToast({
title: "取消订阅失败: ",
topic,
icon: "none",
duration: 2000,
});
}
});
} catch (error) {}
},
// 发布消息
publishTopic(topic, message) {
try {
if (!connected) return;
this.globalData.client.publish(topic, message, { qos: 0 }, (err) => {
if (!err) {
console.log("发布消息成功: ", topic, message);
} else {
console.error("发布消息失败:", err);
wx.showToast({
title: "发布消息失败: " + topic,
icon: "none",
duration: 2000,
});
}
});
} catch (error) {}
},
// 断开连接
disconnectMQTT() {
try {
if (this.globalData.client && connected) {
this.globalData.client.end();
connected = false;
this.globalData.mqttConnected = false;
console.log("已断开连接end");
}
} catch (error) {}
},
globalData: {},
});
- clientId :需要提供一个唯一的
clientId - username: 实际用户名
- password:实际用密码
- ca:配置CA证书验证
- rejectUnauthorized : 强制验证服务器证书,不需要证书可设为false,不传
ca - connectUrl : "
wxs://example.com:8083/mqtt",微信需要使用wxs安全协议,小程序后台》开发管理》服务器域名》socket合法域名配置的是wss://example.com - 断开连接:当不再需要与 MQTT 服务器保持连接时,可调用相应方法断开连接,以释放资源
- 订阅主题:允许小程序订阅特定的主题,从而接收来自该主题的消息
- 取消订阅:当不需要接收某个主题的消息时,可进行取消订阅操作
- 发布消息:小程序可以向指定的主题发布消息,与其他设备或服务进行通信
使用,我是在页面中的onShow方法中调用MQTT,然后订阅主题。
javascript
let app = getApp();
Page({
onShow() {
this.initMqttFun();
},
initMqttFun() {
app.initMqtt(() => {
if (app.globalData.mqttConnected) {
let topic1 = `订阅主题`;
let topic2 = `订阅主题`;
app.subscribeTopic(topic1);
app.subscribeTopic(topic2, { qos: 1 });
app.globalData.client.on("message", (topic, message) => {
console.log(topic, message.toString());
});
}
});
},
});
🌟 三、技术难点剖析
-
协议适配问题 :微信小程序对网络通信有严格规范,普通的
WebSocket协议在此场景下受限,必须使用特殊的"wxs"协议来替代标准的WSS协议进行MQTT连接。这一改动增加了开发的复杂性,容易因协议书写错误导致连接失败。 -
ClientID 管理 :如果连接使用的
clientId重复,会出现互相顶掉线的情况。这意味着开发者需要在生成clientId时确保唯一性,通常会结合时间戳等因素动态生成,避免冲突影响正常连接和使用。 -
消息质量服务水平的选择与保障 :
MQTT提供了三种消息传递保证级别(QoS 0、1、2),不同的级别适用于不同的业务场景。在选择合适QoS级别的同时,还要确保在整个通信过程中能够正确处理各种异常情况,以保证消息的准确性和完整性。例如在一些关键数据的传输场景下,可能需要更高的QoS级别来保证数据不丢失且仅被处理一次。 -
跨平台兼容性挑战 :尽管这里主要讨论的是微信小程序环境,但在实际应用中可能会涉及到与其他平台或设备的交互。不同平台的
MQTT实现可能存在差异,这就需要进行充分的测试和调试,以确保跨平台的兼容性良好。特别是在一些复杂的物联网架构中,多种类型的设备都可能参与到基于MQTT的通信体系中,协调好各方的差异至关重要。
⚠️ 四、注意事项汇总
-
域名备案与配置 :如果有自己的
MQTT服务器并且进行了 ICP 备案,那么可以直接按照正规流程在微信公众平台进行配置,无需勾选那些绕过校验的选项。但如果使用的是未备案的测试服务器,就必须按照前述方法勾选相关选项,否则无法建立连接,证书必须有正规CA机构签发(如:阿里云SSL等),自签名证书会被小程序决绝,我就是使用了自签名证书,导致在开发工具中正常使用,手机端不能使用,一直重新链接,查看mqtt.js源码,发现它也是使用wx.connectSocket实现的,我在里面添加了wx.onSocketError才监听到错误errCode: 1004 errMsg: "open fail: _code:8,_msg:TLS handshake failed",想了各种办法后来才发现是证书的问题。微信小程序无法使用1883端口的明文MQTT链接,必须通过加密协议(mqtts://或wss://)及合规端口(443、8883等)实现。 -
错误处理机制完善 :在整个
MQTT连接和使用过程中,不可避免地会遇到各种错误情况,如网络波动导致的连接中断、认证失败等。开发者应当建立健全的错误处理机制,及时捕获并处理这些错误,为用户提供友好提示的同时尽量恢复连接或采取补救措施。 -
性能优化考量 :由于微信小程序运行环境和资源有限,在使用
MQTT时要关注性能优化。合理设置重连间隔时间、控制消息频率和大小等参数,避免频繁的网络请求和过多的数据处理导致小程序卡顿甚至崩溃。 -
安全性重视 :除了遵循微信小程序的安全规范外,还应考虑
MQTT自身的安全机制。例如可以根据实际需求启用用户名密码认证等功能,防止非法设备接入和数据泄露风险。尤其是在涉及敏感信息的传输场景下,加密传输等方式也应纳入考虑范围。
🔄 五、推荐使用版本介绍
对于原生微信小程序而言,推荐使用 MQTT.js v4.2.1、v4.2.0、v4.1.0 和 v2.18.8 这几个版本。而对于使用 uniapp 框架搭建的微信小程序项目,则可选择 v4.1.0 和 v2.18.8。如果在调试过程中遇到真机调试有问题但模拟器正常的情况,可以尝试切换不同的 mqtt.js 版本来解决。