/// examples/resume — QoS2 中断重连(会话恢复)演示。
///
/// 纯计算路径,不连真实 broker。完整时间线见 moon.pkg 顶部注释。
fn main() -> Unit {
let qos2 = match @protocol.qos_from_int(2) {
Some(q) => q
None => return
}
println("=== moon-mqtt · QoS2 中断重连演示 ===")
println("")
// 1) 客户端发一条 QoS2 PUBLISH,store 记录在途(WaitPubrec)
let mut store = @session.SessionStore::new()
let seed = @protocol.PublishPacket::new(
"device/accel",
[0x01, 0x02, 0x03, 0x04],
qos2,
false,
false,
0,
)
let (s1, pid) = @session.record_outbound(store, seed)
store = s1
println("[1] 发出 QoS2 PUBLISH packet_id=" + pid.to_string() +
" topic=device/accel;store 进入 WaitPubrec")
println(" inflight 计数 = " + store.inflight_count().to_string())
println("")
// 2) 模拟 broker 回 PUBREC —— 但此刻网络断开,客户端没收到 PUBCOMP
// (现实中:TCP 被对端 reset,或设备掉电)
println("[2] 模拟:broker 回了 PUBREC,但客户端在等 PUBCOMP 前断线")
let (s2, _pubrel_bytes) = @session.on_pubrec(store, pid)
store = s2
println(" 本地收到 PUBREC,store 推进到 WaitPubcomp(已生成 PUBREL 待发)")
println("")
// 3) 设备重启 / 重连。从持久化(实际项目里是文件)读回 store 快照。
// 这里用 from_parts 模拟:在途条目仍在,next_id 游标不变。
println("[3] 重连后用 SessionStore.replay() 取待重放帧:")
let frames = @session.replay(store)
println(" replay 返回 " + frames.length().to_string() + " 帧:")
let mut i = 0
for f in frames {
i = i + 1
println(" 帧 " + i.to_string() + ":" + f.length().to_string() +
" 字节(WaitPubcomp 状态应产出 PUBREL,DUP PUBLISH 已在 WaitPubrec 阶段完成)")
}
println("")
// 4) 重连后把 PUBREL 发给 broker,broker 回 PUBCOMP
println("[4] 重连后重发 PUBREL,broker 回 PUBCOMP")
store = @session.on_pubcomp(store, pid)
println(" on_pubcomp 后 inflight 计数 = " + store.inflight_count().to_string())
println("")
// 5) 再 replay 应为空
let after = @session.replay(store)
println("[5] 握手完成。再 replay 返回 " + after.length().to_string() + " 帧(应为 0)")
println("")
println("=== 结论:QoS2 exactly-once 在断线重连后通过 DUP 重发 + PUBREL 幂等语义")
println(" 保证不丢失、不重复。见 session/persistent.mbt 单测。 ===")
}