moon-mqtt:MoonBit 生态的 MQTT 3.1.1 客户端库——QoS0/1/2、持久会话恢复、core/ 纯计算状态机(四后端可测)、浏览器 WebSocket 高层 API;5.0 为后续路线
| 宿主 | 包 | 形态 |
|---|---|---|
| 浏览器(js 后端) | browser/ | 回调式,publish 支持 QoS0/1/2,断线 1 秒自动重连 |
| native | client/ | 阻塞式,自动心跳;调用 reconnect() 后自动 replay |
| 纯计算层 | protocol/ session/ core/ | wasm / wasm-gc / js / native 四后端可测 |
moon add Duckweed/moon-mqtt@0.3.3import {
"Duckweed/moon-mqtt",
}pwsh scripts/build-demo.ps1 # moon build --target js examples/browser-demo,产物复制到 docs/demo/
python -m http.server 8765 # 在仓库根目录
# 浏览器打开 http://127.0.0.1:8765/docs/demo/// your-app/main.mbt
import {
"Duckweed/moon-mqtt/browser" @browser,
"Duckweed/moon-mqtt/protocol" @protocol,
}
fn main() -> Unit {
let client = @browser.connect_persistent("broker.emqx.io", 8084, "my-browser-client")
@browser.on_connect(client, fn() {
println("connected")
@browser.subscribe(client, "sensors/#", @protocol.qos1())
})
// SUBACK 之后再发,broker 已经建好订阅,能收到自己发的消息
@browser.on_suback(client, fn() {
@browser.publish(client, "sensors/hello", [0x68, 0x69], @protocol.qos1())
})
@browser.on_message(client, fn(topic, payload) {
println("recv " + topic + " " + payload.length().to_string() + " bytes")
})
}| 函数 | 说明 |
|---|---|
| connect(host, port, client_id) | WSS 连上去,发 CONNECT,clean_session=true |
| connect_opts(host, port, opts) | 自定义 CONNECT:认证、遗嘱、clean_session |
| connect_persistent(host, port, client_id) | clean_session=false,重连后拿回同一个会话 |
| on_connect(client, handler) | 收到 CONNACK 后回调 |
| on_suback(client, handler) | 收到 SUBACK 后回调,适合在这里开始 publish |
| on_message(client, handler) | 收到 PUBLISH 时回调 (topic, payload) |
| subscribe(client, topic, qos) | 订阅主题 |
| publish(client, topic, payload, qos) | 发布消息,QoS0/1/2 |
| disconnect(client) | 发 DISCONNECT 并关闭连接 |
[browser] session_present=0
[demo] connected, subscribing sensors/# QoS1
[browser] suback
[demo] suback, publishing QoS1 sensors/hello
[demo] recv topic=sensors/hello payload=2 bytes[browser] reconnect attempt #1 in 1s
[browser] session_present=1, replay 0 frame(s)
[demo] connected, subscribing sensors/# QoS1| 示例 | 跑法 | 做什么 |
|---|---|---|
| examples/pubsub | moon run examples/pubsub | 报文编解码往返,不连 broker |
| examples/resume | moon run examples/resume | QoS2 中断重连时间线,纯计算 |
| examples/browser-demo | pwsh scripts/build-demo.ps1 | 浏览器端到端,页面在 docs/demo/ |
| examples/ws-spike | moon build --target js examples/ws-spike | 裸 WebSocket spike,留作协议层对照 |
let qos = match @protocol.qos_from_int(1) { Some(q) => q None => return }
let pkt = @protocol.PublishPacket::new(
"sensors/temp", [0x32, 0x33], qos, false, false, 7,
)
let bytes = @protocol.encode_publish(pkt)
match @protocol.decode_publish(bytes) { ... }// 自定义 CONNECT(带认证 + 遗嘱 + 持久会话)
let opts = @protocol.ConnectPacket::new("demo-client")
.with_keep_alive(30)
.with_clean_session(false)
.with_auth("user", [0x70, 0x61, 0x73, 0x73]) // "pass"
.with_will("status", "offline".to_bytes(), @protocol.qos1(), false)
let client = @client.connect_opts("broker.hivemq.com", 1883, opts)?
client.subscribe_with_handler("sensors/#", @protocol.qos1(), fn(topic, payload) {
println("recv " + topic)
})?
// 每 1 秒 poll 一次,累计到 keep_alive 自动发 PINGREQ
match client.poll_loop(600, 1000) {
Ok(_) => ()
Err(_) => {
let client = client.reconnect()?
println("reconnected, session_present=" + client.is_session_present().to_string())
}
}
client.disconnect()?moon build --target js examples/ws-spike
node "_build\js\debug\build\examples\ws-spike\ws-spike.js"
矢量图在 docs/assets/architecture.svg。颜色区分应用、宿主 API、传输层和纯计算层。
sequenceDiagram
participant B as Broker
participant G as 胶水层 (WebSocket / C FFI)
participant C as core.ClientCore
participant A as 应用回调
G->>C: start()
C-->>G: CONNECT 字节
G->>B: send CONNECT
B-->>G: CONNACK 字节
G->>C: feed(bytes)
C-->>G: [Connack] + 出站 []
G->>A: on_connack(session_present)
B-->>G: PUBLISH (QoS2) 字节
G->>C: feed(bytes)
C-->>G: [Publish] + [PUBREC]
G->>A: on_publish(topic, payload)
G->>B: send PUBREC
B-->>G: PUBREL
G->>C: feed(bytes)
C-->>G: [] + [PUBCOMP]
G->>B: send PUBCOMP| MQTT 3.1.1 CONNECT 字段 | 支持 |
|---|---|
| Client Identifier | ✅ 必填 |
| Keep Alive | ✅ 默认 60s,可配 |
| Clean Session | ✅ 默认 true,可配 |
| Username / Password | ✅ 可选 |
| Will Message(topic / payload / QoS / retain) | ✅ 可选 |
| 持久会话恢复 | ✅ 在途 QoS1/2 PUBLISH 由 SessionStore 记录,重连后 replay() 按 §4.4 重发 |
moon check
moon check --target js --deny-warn # 0 警告
moon test --target wasm-gc # 82/82
moon test --target native # 107/107
moon run examples/pubsub
moon run examples/resume
pwsh scripts/build-demo.ps1 # 浏览器 demo# 1. 起本地 broker
mosquitto -p 1883 -v
# 2. 另开终端订阅
mosquitto_sub -h 127.0.0.1 -t 'sensors/#' -v
# 3. 跑 MoonBit 程序(connect → publish → subscribe → ping → disconnect)
# 代码见上文「Native TCP」Install
Download zipmoon-mqtt:MoonBit 生态的 MQTT 3.1.1 客户端库——QoS0/1/2、持久会话恢复、core/ 纯计算状态机(四后端可测)、浏览器 WebSocket 高层 API;5.0 为后续路线