最近接到安卓端的需求,要求使用MQTT连接实现设备信息的收发。
可能有兄弟不太清楚 MQTT协议 是什么,简单地说它是一种轻量级的、基于发布/订阅模式的消息传输协议,广泛用于物联网(IoT)领域。
常见的操作就是连接后有N个设备订阅了主题A,这时候任意一台设备对主题A发布了一条信息,则当前N个设备都能收到这条消息。
至于其他的也不用知道的太清楚,反正知道一般是这么玩的就行。
假如你想知道的更为详细,可以询问 Trae。

需求
- 实现MQTT连接
- 实现主题(Topic)订阅
- 能够发送消息
- 能够接收消息
实现方案
首先是安装 mqtt.js 插件和 uuid.js 插件。
npm i mqtt@3.0.0 uuid
注意: 这里一定要安装 mqtt v3.0 版本,我之前装了一个 v5.x 版本,有比较大的 API 改动。项目上时间紧张,遂没有再尝试。(有兴趣的朋友可以考虑用最新版实现)
uuid 只是起到生成 连接ID 和 消息ID 的作用,这里也可以直接使用时间戳的方式,但是为了保证不重复,这里便不再更换了。
// mqttClient.js 对 MQTT 的简单封装
let client = null;
/**
* 初始化 MQTT 连接
* @param {Object} options
* @param {string} options.host - 服务器地址(不带协议)
* @param {number} options.port - 端口(通常 WebSocket 是 8083、8084 或 443)
* @param {string} options.clientId - 客户端 ID(建议唯一)
* @param {string} [options.username] - 用户名
* @param {string} [options.password] - 密码
* @param {boolean} [options.useWss=false] - 是否使用 wss(HTTPS)
*/
export function initMqtt(options) {
const {
host,
port,
clientId,
username = '',
password = '',
useWss = false
} = options;
// 根据平台选择协议(App 和 H5 用 ws/wss,小程序用 wxs/wss)
let protocol = 'ws';
if (useWss) protocol = 'wss';
// #ifdef MP-WEIXIN
protocol = useWss ? 'wxs' : 'wxs';
// #endif
const url = `${protocol}://${host}:${port}/mqtt`; // 注意:路径可能是 / 或 /mqtt,根据你的 broker 配置
const connectOptions = {
clientId: clientId,
username: username,
password: password,
keepalive: 60, // 心跳间隔(秒)
reconnectPeriod: 5000, // 自动重连间隔(毫秒)
connectTimeout: 10000, // 连接超时
clean: true // 是否清除会话
};
// 引入 mqtt(必须用 min 版本,否则小程序报错)
const mqtt = require('mqtt/dist/mqtt.min.js');
client = mqtt.connect(url, connectOptions);
// 监听连接事件
client.on('connect', () => {
console.log('MQTT 连接成功');
});
client.on('reconnect', () => {
console.log('MQTT 正在重连...');
});
client.on('error', (err) => {
console.error('MQTT 连接错误:', err);
});
client.on('close', () => {
console.log('MQTT 连接已关闭');
});
return client;
}
/**
* 订阅主题
* @param {string} topic
* @param {Function} callback - (topic, message) => {}
*/
export function subscribe(topic, callback) {
if (!client) {
console.warn('MQTT 客户端未初始化');
return;
}
client.subscribe(topic, (err) => {
if (!err) {
console.log(`订阅主题成功: ${topic}`);
} else {
console.error('订阅失败:', err);
}
});
// 监听消息(全局监听,需配合 topic 过滤)
client.on('message', (receivedTopic, message) => {
// if (receivedTopic === topic) {
try {
// 尝试解析 JSON
const payload = JSON.parse(message.toString());
callback(receivedTopic, payload);
} catch (e) {
// 非 JSON 消息
callback(receivedTopic, message.toString());
}
// }
});
}
/**
* 发布消息
* @param {string} topic
* @param {string|object} message
*/
export function publish(topic, message) {
if (!client) {
console.warn('MQTT 客户端未初始化');
return;
}
const payload = typeof message === 'object' ? JSON.stringify(message) : message;
client.publish(topic, payload, {
qos: 1
}, (err) => {
if (err) {
console.error('发布失败:', err);
} else {
console.log(`发布成功: ${topic}`, payload);
}
});
}
/**
* 断开连接
*/
export function disconnect() {
if (client) {
client.end(true); // true 表示强制断开
client = null;
console.log('MQTT 已断开');
}
}
我让 Trae 基于网上的封装简单进行了修改,生成了上述封装Js文件。
注意:在监听消息的部分,如果你的 Topic 是固定的,则请将 if (receivedTopic === topic) 的注释打开,如果不是,则注释掉,后续有详解。
现在进入到 page/index/index.vue 文件中
import {initMqtt, subscribe, publish, disconnect} from './mqttClient.js';
export default {
data() {
return {
host: 'broker.emqx.io',
messages: []
}
},
mounted() {
this.connect()
},
beforeDestroy() {
this.disconnect()
},
methods: {
// 连接
connect() {
const uuid = uuidv4();
const client = initMqtt({
host: this.host,
port: 8083,
clientId: uuid,
username: '',
password: '',
useWss: false // 如果是 HTTPS/WSS,设为 true
});
if (client) {
this.subscribeTopic();
}
},
// 订阅 test/topic 主题
subscribeTopic() {
subscribe('test/topic', (topic, message) => {
// 接收到的消息
const msg = JSON.stringify(message);
this.messages.unshift(msg);
});
},
// 订阅 带通配符 的主题
subscribeTopics() {
subscribe('+/topic', (topic, message) => {
// 接收到的消息
const msg = JSON.stringify(message);
this.messages.unshift(msg);
});
},
// 发送消息到 test/topic 主题
publishMessage() {
publish('test/topic', {
name: 'uni-app',
time: new Date().toISOString(),
msg: '测试信息'
});
},
disconnect() {
disconnect();
}
}
}
注意:在卸载页面的时候建议将 MQTT连接 断开,或者在 uni-app 中使用也可以使用 unload 方法断开连接。
另外订阅主题可以采用常规的,确定性的 Topic 主题名称,也可以采用 通配符(+) 的主题名称。
Trae还提醒我一定要注意,假如你的 Topic 为:
123/456/report 则你的通配符主题名称为 +/+/report
假如你的 Topic 为:
/123/456/report 则你的通配符主题名称为 +/+/+/report,因为他的第一位是 空字符串。
结论
在开发 IoT 设备时这个协议用的比较广泛,如果不接触这个方面一般不会用上这个。
另外 MQTT 本身自带心跳和自动重连机制,大致上相当于 WebSocket,这个部分一般不需要做特殊开发。