4. WebSocket桥接MQTT
任务目标
本案例将配置EMQX(或Mosquitto + WebSocket插件)以支持MQTT over WebSocket,并提供一个纯HTML+JavaScript的浏览器客户端,使其能够通过WebSocket协议连接到MQTT Broker,完成消息的订阅与发布。
环境依赖
- EMQX Broker(v4.x或v5.x均可),默认已启用WebSocket监听端口
8083(ws)和8084(wss) - 浏览器(Chrome/Firefox/Edge均可,需支持原生WebSocket API)
- MQTT.js客户端库(通过CDN引入,无需本地安装)
实现方案
MQTT over WebSocket的本质是将MQTT协议数据包封装在WebSocket帧中传输。EMQX默认在8083端口提供此能力,无需额外配置。浏览器端使用MQTT.js库,通过ws://协议地址连接Broker,即可像原生MQTT客户端一样收发消息。
关键完整代码
创建一个mqtt_ws_client.html文件,内容如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>MQTT over WebSocket 浏览器客户端</title>
<script src="https://unpkg.com/mqtt@5.3.5/dist/mqtt.min.js"></script>
</head>
<body>
<h3>MQTT over WebSocket 测试工具</h3>
<div>
<label>Broker地址: </label>
<input id="brokerUrl" value="ws://localhost:8083/mqtt" style="width:250px">
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>
<span id="connStatus">未连接</span>
</div>
<div style="margin-top:10px">
<label>订阅主题: </label>
<input id="subTopic" value="test/topic">
<button onclick="subscribe()">订阅</button>
</div>
<div style="margin-top:10px">
<label>发布主题: </label>
<input id="pubTopic" value="test/topic">
<label>消息: </label>
<input id="pubMsg" value="Hello MQTT over WS">
<button onclick="publish()">发布</button>
</div>
<div style="margin-top:10px">
<h4>消息日志:</h4>
<div id="log" style="border:1px solid #ccc;height:200px;overflow-y:auto;padding:5px"></div>
</div>
<script>
let client = null;
function log(msg) {
const div = document.getElementById('log');
const p = document.createElement('p');
p.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
div.appendChild(p);
div.scrollTop = div.scrollHeight;
}
function connect() {
const url = document.getElementById('brokerUrl').value.trim();
if (!url) { log('请输入Broker地址'); return; }
client = mqtt.connect(url, {
clientId: 'browser_' + Math.random().toString(16).substr(2, 8),
clean: true,
connectTimeout: 5000,
reconnectPeriod: 0
});
client.on('connect', () => {
document.getElementById('connStatus').textContent = '已连接';
document.getElementById('connStatus').style.color = 'green';
log(`连接成功: ${url}`);
});
client.on('message', (topic, payload) => {
log(`收到消息 [${topic}]: ${payload.toString()}`);
});
client.on('error', (err) => {
log(`连接错误: ${err.message}`);
document.getElementById('connStatus').textContent = '连接失败';
document.getElementById('connStatus').style.color = 'red';
});
client.on('close', () => {
document.getElementById('connStatus').textContent = '已断开';
document.getElementById('connStatus').style.color = 'gray';
log('连接已关闭');
});
}
function disconnect() {
if (client) {
client.end(true);
client = null;
log('主动断开连接');
}
}
function subscribe() {
if (!client || !client.connected) { log('请先连接'); return; }
const topic = document.getElementById('subTopic').value.trim();
if (!topic) { log('请输入订阅主题'); return; }
client.subscribe(topic, (err) => {
if (err) { log(`订阅失败: ${err.message}`); }
else { log(`已订阅: ${topic}`); }
});
}
function publish() {
if (!client || !client.connected) { log('请先连接'); return; }
const topic = document.getElementById('pubTopic').value.trim();
const msg = document.getElementById('pubMsg').value;
if (!topic) { log('请输入发布主题'); return; }
client.publish(topic, msg, { qos: 0 }, (err) => {
if (err) { log(`发布失败: ${err.message}`); }
else { log(`已发布到 [${topic}]: ${msg}`); }
});
}
</script>
</body>
</html>
运行与验证
- 启动Broker:确保EMQX已启动,默认WebSocket监听端口为
8083。可通过访问http://localhost:18083(EMQX Dashboard)确认状态。 - 打开页面:直接用浏览器打开
mqtt_ws_client.html文件。 - 连接Broker:保持默认地址
ws://localhost:8083/mqtt,点击“连接”,状态变为“已连接”且日志显示连接成功。 - 订阅主题:输入
test/topic,点击“订阅”,日志显示“已订阅”。 - 发布消息:在发布区域输入相同主题和消息内容,点击“发布”。
- 验证闭环:日志中应出现“已发布”记录,随后立即出现“收到消息 [test/topic]: 你输入的内容”。这证明消息经MQTT Broker通过WebSocket完成了浏览器客户端的收发闭环。
常见问题
- 连接失败:检查EMQX是否运行,以及
8083端口是否被防火墙拦截。若使用wss,需配置证书。 - 跨域限制:EMQX默认允许所有跨域请求,若自定义配置需在
etc/emqx.conf中设置listener.ws.external.allow_origin为*。 - 路径问题:EMQX的WebSocket默认路径为
/mqtt,若使用其他Broker(如Mosquitto),需确认其WebSocket路径配置。