【aardio】零依赖纯 TCP 快速搞定 MQTT 消息发布调试
【aardio】不到百行代码,零依赖纯 TCP 快速搞定 MQTT 消息发布调试
在进行工业控制、物联网(IoT)开发或者自动化测试时,我们经常需要快速向 MQTT 服务器发送一条指令来触发设备动作。虽然市面上有很多功能完整的 MQTT 库,但对于临时调试、发送单条指令的场景来说,引入庞大的第三方库或配置复杂的环境显得有些大材小用。
本文分享一段用 aardio 实现的硬核脚本。它直接基于纯 TCP 客户端(wsock.tcp.client),在代码中手动完成了 MQTT 二进制报文的拼装与解析。不到 100 行代码,实现全绿色、零依赖、即开即用的 MQTT QoS 0 消息发布。
注意本文代码由 AI 自动生成,经过测试非常好用。
🚀 核心优势
- 零外部依赖:仅使用 aardio 自带的内置库,拷贝代码即可直接运行。
- 超轻量高效:没有复杂的封装,直面 MQTT 协议底层,内存占用极小。
- 非常适合调试:在工业控制台体调试、自动化脚本或者临时交互时,可作为“微型瑞士军刀”使用。
💾 完整实现代码
你可以直接将以下代码复制到 aardio 编辑器中,修改底部的服务器 IP、主题(Topic)和负载(Payload)即可直接运行:
import console;
import wsock.tcp.client;
// ==========================================
// ====== MQTT 协议底层报文辅助函数 ======
// ==========================================
// 1. 剩余长度编码(MQTT 变长编码:每字节低7位存数据,最高位1表示后续还有字节)
var encodeRemainingLength = function(length){
var buf = raw.buffer(4);
var idx = 0;
while(true){
var b = length % 128;
length = length >> 7;
if(length > 0) b = b | 0x80; // 最高位置 1
buf[idx+1] = b;
idx = idx + 1;
if(length <= 0) break;
}
return tostring(raw.slice(buf, 1, idx));
}
// 2. 大端序 2 字节长度前缀 + 字符串(MQTT 字符串标准格式)
var packLenStr = function(str){
var len = #str;
return string.pack((len >> 8) & 0xFF, len & 0xFF) ++ str;
}
// 3. 构建 MQTT CONNECT 控制报文
var buildConnect = function(clientId){
// 可变报头:协议名长度+协议名("MQTT") + 协议级别(4) + 连接标志(0x02 Clean Session) + 保持连接时间(60s)
var varHeader = packLenStr("MQTT")
++ string.pack(4, 0x02)
++ string.pack((60 >> 8) & 0xFF, 60 & 0xFF);
// 有效载荷:客户端 ID
var remaining = varHeader ++ packLenStr(clientId);
// 固定报头:0x10 (CONNECT 类型) + 剩余长度 + 载荷内容
return string.pack(0x10) ++ encodeRemainingLength(#remaining) ++ remaining;
}
// 4. 构建 MQTT PUBLISH 控制报文 (QoS 0)
var buildPublish = function(topic, message){
// 载荷内容:主题名 + 消息正文
var remaining = packLenStr(topic) ++ message;
// 固定报头:0x30 (PUBLISH QoS 0) + 剩余长度 + 载荷内容
return string.pack(0x30) ++ encodeRemainingLength(#remaining) ++ remaining;
}
// ==========================================
// ====== 核心功能:全流程发布消息 ======
// ==========================================
var mqttPublish = function(host, port, topic, payload){
// 自动生成基于时间戳的唯一 ClientID
var clientId = "aardio-" ++ tostring(time.tick());
var tcp = wsock.tcp.client();
tcp.setTimeouts(5000, 5000); // 设置发送与接收超时
// 【步骤 1】建立物理 TCP 连接
var ok, err = tcp.connectTimeout(host, port, 5);
if(!ok) return null, "连接失败: " + (err || "未知错误");
// 【步骤 2】发送 CONNECT 报文并等待握手响应 (CONNACK)
tcp.write(buildConnect(clientId));
var connack = tcp.readEx(4); // CONNACK 固定为 4 字节
if(!connack || #connack < 4){
tcp.close();
return null, "未收到响应或连接被代理拒绝 (CONNACK 为空)";
}
// 解析返回码 (第 4 字节为连接返回码,0 表示接受连接)
var retCode = connack[4];
if(retCode != 0){
tcp.close();
return null, "MQTT 连接失败,服务器返回码: " + retCode;
}
// 【步骤 3】发送 PUBLISH 报文 (QoS 0 不需要等待服务器应答)
tcp.write(buildPublish(topic, payload));
// 【步骤 4】发送 DISCONNECT 断开连接报文 (优雅退出)
thread.delay(200); // 稍微等待,确保发布缓冲区数据已送出
tcp.write(string.pack(0xE0, 0x00)); // 0xE0 表示 DISCONNECT 报文
thread.delay(100); // 留给服务器处理关闭的时间
tcp.close();
return true;
}
// ==========================================
// ====== 运行与调试示例 ======
// ==========================================
console.open();
var brokerIp = "192.168.97.34"; // 你的 MQTT Broker 地址
var port = 1883;
var topic = "lua/cmd/run";
var jsonPayload = '{"fn": "voltage_all_phase_test.lua"}';
print("正在尝试发布消息...");
var ok, err = mqttPublish(brokerIp, port, topic, jsonPayload);
if(ok) {
print("【成功】MQTT 消息已成功送达 Broker!");
} else {
print("【失败】原因:", err);
}
console.pause();
🛠️ 代码技术点解析
对于对底层协议感兴趣的同学,这段代码有几个精妙的细节值得学习:
1. 变长长度编码(encodeRemainingLength)
MQTT 协议为了节省网络带宽,其“剩余长度”字段采用了可变长度的编码方式(类似于一种压缩算法)。每个字节的最高位(bit 7)作为标志位,如果为 1,代表后面还有一个字节参与表示长度;如果为 0,则代表这是长度字段的最后一个字节。
代码中利用 length % 128 和位移操作 length >> 7 完美手工实现了这一解析逻辑。
2. 位操作与字符串组装
MQTT 报文中的双字节整数(如字符串长度、KeepAlive 时间)均要求大端序(Big-Endian)传输。
代码中利用 (len >> 8) & 0xFF 取出高 8 位,len & 0xFF 取出低 8 位,再利用 aardio 独具特色的字符串连接符 ++ 直接将字节与原始字符串高速拼接,效率极高。
3. 优雅下线
在 PUBLISH 发送完毕后,代码并未粗暴地直接关闭 TCP 连接,而是发送了 0xE0, 0x00(即 MQTT 的 DISCONNECT 报文)。这样做可以明确告知 MQTT Broker 客户端是主动正常下线,避免 Broker 将其误判为异常断开而触发遗嘱消息(Will Message)。
💡 适用场景与局限性
- 适用:发送频率低、单次单条的控制指令下发、轻量化上位机测试工具、无需接收订阅消息的单向上报。
- 局限:本代码针对“快发快取”的调试场景设计,仅支持 QoS 0(最多交付一次)。如果你的业务场景需要高可靠性的断线重连、长时间维持长连接心跳(KeepAlive PING)、或者复杂的 QoS 1/2 消息应答与主题订阅,建议在正式项目中引入更完整的工业级 MQTT 客户端组件。
浙公网安备 33010602011771号