微信小程序中使用 MQTT 实现实时通信:技术难点与实践指南

📌 一、引言

MQTT(Message Queuing Telemetry Transport)是一种轻量级的机器对机器(M2M)通信协议,专为物联网和低带宽环境设计。其核心特点是采用发布/订阅模式,具有低功耗、高效性和高可靠性等优点,非常适合在资源有限的设备上运行。在微信小程序中集成 MQTT 可以实现实时数据传输等功能。本文将详细介绍如何在微信小程序中使用 MQTT,并分析其中的技术难点及注意事项。

🛠️ 二、实施步骤与关键代码解析
  1. 引入 MQTT 库 :微信小程序本身不直接提供 MQTT 客户端功能,因此需要借助第三方库来实现。目前常用的 mqtt.js 版本有多个,如 v4.1.0 等。开发者可以根据项目需求选择合适的版本,并将其拷贝至项目根目录下新建的 utils 文件夹中,随后通过 import mqtt from "../../utils/mqtt.min.js" 方式引入mqtt.js v4.1.0 下载地址

  2. 建立连接:微信小程序安全要求较高,与后台服务器之间的通讯必须使用 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());
	      });
	    }
	  });
	},
});
🌟 三、技术难点剖析
  1. 协议适配问题 :微信小程序对网络通信有严格规范,普通的 WebSocket 协议在此场景下受限,必须使用特殊的 "wxs" 协议来替代标准的 WSS 协议进行 MQTT 连接。这一改动增加了开发的复杂性,容易因协议书写错误导致连接失败。

  2. ClientID 管理 :如果连接使用的 clientId 重复,会出现互相顶掉线的情况。这意味着开发者需要在生成 clientId 时确保唯一性,通常会结合时间戳等因素动态生成,避免冲突影响正常连接和使用。

  3. 消息质量服务水平的选择与保障MQTT 提供了三种消息传递保证级别(QoS 0、1、2),不同的级别适用于不同的业务场景。在选择合适 QoS 级别的同时,还要确保在整个通信过程中能够正确处理各种异常情况,以保证消息的准确性和完整性。例如在一些关键数据的传输场景下,可能需要更高的 QoS 级别来保证数据不丢失且仅被处理一次。

  4. 跨平台兼容性挑战 :尽管这里主要讨论的是微信小程序环境,但在实际应用中可能会涉及到与其他平台或设备的交互。不同平台的 MQTT 实现可能存在差异,这就需要进行充分的测试和调试,以确保跨平台的兼容性良好。特别是在一些复杂的物联网架构中,多种类型的设备都可能参与到基于 MQTT 的通信体系中,协调好各方的差异至关重要。

⚠️ 四、注意事项汇总
  1. 域名备案与配置 :如果有自己的 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://)及合规端口(4438883等)实现。

  2. 错误处理机制完善 :在整个 MQTT 连接和使用过程中,不可避免地会遇到各种错误情况,如网络波动导致的连接中断、认证失败等。开发者应当建立健全的错误处理机制,及时捕获并处理这些错误,为用户提供友好提示的同时尽量恢复连接或采取补救措施。

  3. 性能优化考量 :由于微信小程序运行环境和资源有限,在使用 MQTT 时要关注性能优化。合理设置重连间隔时间、控制消息频率和大小等参数,避免频繁的网络请求和过多的数据处理导致小程序卡顿甚至崩溃。

  4. 安全性重视 :除了遵循微信小程序的安全规范外,还应考虑 MQTT 自身的安全机制。例如可以根据实际需求启用用户名密码认证等功能,防止非法设备接入和数据泄露风险。尤其是在涉及敏感信息的传输场景下,加密传输等方式也应纳入考虑范围。

🔄 五、推荐使用版本介绍

对于原生微信小程序而言,推荐使用 MQTT.js v4.2.1v4.2.0v4.1.0v2.18.8 这几个版本。而对于使用 uniapp 框架搭建的微信小程序项目,则可选择 v4.1.0v2.18.8。如果在调试过程中遇到真机调试有问题但模拟器正常的情况,可以尝试切换不同的 mqtt.js 版本来解决。

相关推荐
00后程序员张14 小时前
HTTP抓包工具推荐,Fiddler配置方法、代理设置与使用教程详解(开发者必学网络调试技巧)
网络·http·ios·小程序·fiddler·uni-app·webview
悟空码字15 小时前
微信小程序管理系统,代运营3600+医院小程序
微信·小程序·编程·软件开发
浔川python社16 小时前
《Python 小程序编写系列》(第三部):简易文件批量重命名工具
python·小程序·apache
一 乐17 小时前
个人博客|博客app|基于Springboot+微信小程序的个人博客app系统设计与实现(源码+数据库+文档)
java·前端·数据库·spring boot·后端·小程序·论文
会有钱的-_-18 小时前
基于微信小程序的场景解决
微信小程序·小程序·css3
喵喵侠w19 小时前
uni-app微信小程序相机组件二次拍照白屏问题的排查与解决
前端·数码相机·微信小程序·小程序·uni-app
G佳伟19 小时前
微信小程序实现长按复制选中文字的效果
微信小程序·小程序·notepad++
汤姆yu20 小时前
基于微信小程序的特色农产品交易系统
微信小程序·小程序
毕设源码-赖学姐20 小时前
【开题答辩全过程】以 赣农乐微信小程序为例,包含答辩的问题和答案
微信小程序·小程序