fix(reconnect): 合规接收端被周期性误判「链路已死」(v0.2.2)+ 可注入计时覆盖退避封顶
一、测试抓到的真实缺陷(T-AND-017)
契约 §3.6 明确:「接收端收到任何一帧都算活动,ping **无需额外应答**即可保活,
pong 只用于诊断」—— 即"合规但不回 pong"的接收端是**合法**的。
但 PoseWebSocketClient.onOpen 里写的是 lastPongAtMs = timing.nowMs(),
于是心跳循环的守卫 if (lastPongAtMs > 0 && silentFor > limit) **恒为真**:
对任何接收端都会「判定链路已死 → 主动断链 → 重连」,默认参数
(heartbeatIntervalMs=5000)下即**每 15 秒断一次**,正是用户抱怨的「频繁重连」。
为什么以前没暴露:我们自己的接收端(Windows PoseServer)**会**回 pong
(T-WIN-020 实测 9 次 ping 全部收到 pong),这条路径在自家联调里永远走不到。
修复:onOpen 复位 lastPongAtMs = 0L(0 = 本连接还没收到过 pong),
只有「曾经回过 pong、之后又不回了」才判定链路已死 —— 这正是该诊断本来的意图。
二、可注入计时 Timing(时钟 + 等待)
契约 §7.1 的退避序列 0.5s→1→2→4→8→15s 封顶若要端到端验证,真实等待约 30 秒;
「连续 3 个心跳周期没收到 pong」也要等几十秒。抽成接口后:
* 生产默认 SystemTiming = 真实时钟 + 真实 delay,行为与抽取前完全一致
(10 处 System.currentTimeMillis() 与 2 处 delay() 改走 timing);
* 测试注入「可控时钟 + 立即返回的等待」,毫秒级跑完整条序列。
三、新增 4 项 JVM 用例(PoseWebSocketBackoffTest,无需设备)
* 退避序列在 15 秒封顶且不无限增长:连续失败下断言第 1..8 次等待分别落在
500/1000/2000/4000/8000/15000/15000/15000 的 ±20% 区间内,且真实耗时 < 5 秒;
* 等待走注入实现:前三次重连真实耗时 < 1.5 秒(真实退避需 3.5 秒);
* **合规但从不回 pong 的接收端不会被判定链路已死**(修复前此处失败):
走过 6 个心跳周期仍为 streaming、无「判定链路已死」日志;
* 曾经回过 pong 之后停止回复时判定链路已死并重连:诊断仍然有效。
实测:./gradlew :app:testDebugUnitTest → 7 个测试类 / 68 项用例 / 0 失败(原 64)。
build/verify.sh 全绿(签名、APK 元数据 0.2.2/202、native 体检、ABI、R8、dex)。
版本号 0.2.1/201 → 0.2.2/202。
写测试踩的两个坑(已写入任务文档):
1. 不要在瞬时重连下读 stats.nextRetryMs(等待立即返回后已被清零,与重连循环竞争),
等待时长应取注入实现记录的事实;
2. 虚拟时钟需要一个真实让出点(FakeTiming.await 里 2ms 真实 delay),
否则心跳循环会在网络回调落地前把时钟推过阈值。
This commit is contained in:
+17
-2
@@ -1,5 +1,5 @@
|
||||
================================================================
|
||||
OpenDance Studio -- Android 辅助 App v0.2.1 使用说明
|
||||
OpenDance Studio -- Android 辅助 App v0.2.2 使用说明
|
||||
================================================================
|
||||
|
||||
手机端姿态采集器:用摄像头做端侧姿态推理,把 33 个骨骼关键点通过
|
||||
@@ -125,6 +125,21 @@ WebSocket 推送给 Windows 客户端,由后者驱动 VRM 角色并实时评
|
||||
App 运行页 「人体识别」为「已识别」,出现骨架叠加(黄点好、红点置信度低)
|
||||
Windows 端 显示已连接并收到关键点,VRM 模型随动作实时动起来
|
||||
|
||||
6.5) v0.2.2 修订
|
||||
|
||||
1. 修一处真实缺陷(T-AND-017): 契约 §3.6 明确「ping 无需额外应答即可保活,
|
||||
pong 只用于诊断」,即"合规但不回 pong"的接收端是合法的;但客户端的
|
||||
onOpen 里把 lastPongAtMs 设成了当前时刻,于是"连续 3 个心跳周期没收到 pong
|
||||
就判定链路已死"对任何接收端都会触发 —— 默认参数下每 15 秒主动断一次并重连,
|
||||
正是"频繁重连"的症状。现改为 0(= 本连接还没收到过 pong),
|
||||
只有"曾经回过 pong、之后又不回了"才判定链路已死。
|
||||
2. 新增可注入计时 Timing(时钟 + 等待),生产默认与原来完全一致;
|
||||
测试注入可控时钟后,契约 §7.1 的退避序列(0.5→1→2→4→8→15s 封顶)能在
|
||||
毫秒级端到端验证,不再需要等 30 秒。
|
||||
3. 新增 4 项 JVM 用例(无需设备):退避封顶序列逐值断言、注入实现不产生真实等待、
|
||||
合规不回 pong 不被误判、回过 pong 后停止回复仍能正确诊断。
|
||||
4. 单元测试 68 项(原 64)。
|
||||
|
||||
6.5) v0.2.1 修订
|
||||
|
||||
1. 重连链路的真实 WebSocket 集成测试(T-AND-016): 用 MockWebServer 的
|
||||
@@ -157,7 +172,7 @@ WebSocket 推送给 Windows 客户端,由后者驱动 VRM 角色并实时评
|
||||
依赖闭包(DT_NEEDED,非系统库必须同 ABI 自带)、页对齐(16 KB 兼容)。
|
||||
|
||||
0) 发布包级验收(对**发布包**而不是本地构建;文档 §7.5)
|
||||
bash build/accept-release.sh dist-release/opendance-android-v0.2.1.zip
|
||||
bash build/accept-release.sh dist-release/opendance-android-v0.2.2.zip
|
||||
预期末行「发布包级验收全部通过」。它会校验 sha256、解压、核对文件清单、
|
||||
读包内 APK 的 AndroidManifest 版本元数据、核对 ABI 与 MediaPipe native 库、
|
||||
模型资源、dex 里的 PoseLandmarker、验签,以及包内契约快照与仓库是否一致。
|
||||
|
||||
Reference in New Issue
Block a user