Skip to content

feat(online): 增强客户端连接稳定性#3895

Open
zhpy2004 wants to merge 7 commits into
libnoname:mainfrom
zhpy2004:feat/client-connection-robustness
Open

feat(online): 增强客户端连接稳定性#3895
zhpy2004 wants to merge 7 commits into
libnoname:mainfrom
zhpy2004:feat/client-connection-robustness

Conversation

@zhpy2004

@zhpy2004 zhpy2004 commented May 31, 2026

Copy link
Copy Markdown
Contributor

PR受影响的平台

所有平台(浏览器端、电脑端 win/mac、手机端 android/ios)。改动均为联机客户端通用逻辑,不涉及平台特定代码。

诱因和背景

当前联机客户端在连接异常时存在若干稳定性问题:

  • game.send() 仅判断 game.ws 是否存在。当 WebSocket 处于 CLOSING(2) / CLOSED(3) 状态时 game.ws 仍非空,直接 send() 会抛出 InvalidStateError
  • game.connect() 创建 WebSocket 后无超时控制,服务端无响应时用户会无限等待。
  • 断线后直接 game.reload() 粗暴刷新页面,体验差。
  • game.broadcast() 迭代 lib.node.clients 时,client.send() 失败会触发 client.close() 将自身从数组移除,导致迭代中漏发部分客户端。

无关联 issue。

PR描述

  1. game.send() 连接状态检查:发送前增加 game.ws.readyState === 1(OPEN)判断,避免在 CLOSING/CLOSED 状态下调用 send() 抛异常。
  2. game.connect() 连接超时:建立连接后设置 10s 超时,超时仍未 OPEN 则关闭连接并回调失败;onopen/onerror/onclose 中清除该定时器。
  3. 断线自动重连(指数退避)onclose 改为按 1s → 2s → 4s → 8s → 15s 退避重连,共 5 次,全部失败后回退到原有的 game.reload() 行为,保证兼容。用独立的 _status.reconnecting 标志驱动重试链,避免依赖 online/onlineroom 状态——纯大厅场景下二者在重试期间均为 false,否则重试链会中断、重连遮罩卡住。selfclose(被踢 / 房主断开)时取消自动重连并清理 reconnect_info,防止退出后误重连。
  4. game.broadcast() 安全迭代:改为对 lib.node.clients 的快照 [...lib.node.clients] 迭代,避免发送失败移除元素导致漏发。
  5. 重连决策逻辑抽离 + 单测:将重连决策抽成无副作用的纯函数 decideReconnectnoname/util/reconnect.js),onclose 仅依据其返回的 action 执行副作用,并为其新增 vitest 单测。

本 PR 为客户端首次引入 vitest 测试框架(apps/core 此前无测试配置)。

PR测试

  • 新增 noname/util/reconnect.test.js,11 个用例覆盖 decideReconnect 的全部分支:未联机/游戏正常结束 → 不处理;主动退出/无地址/重试耗尽 → 回退刷新;指数退避延迟序列(含 15s 封顶);纯大厅续连(核心修复);maxAttempts 缺省等。
  • pnpm --filter noname test(vitest):1 文件 / 11 用例全部通过。
  • pnpm --filter noname lint(ESLint):通过。
  • pnpm --filter noname build:打包成功,确认新增 import 正确解析、不破坏构建。
  • 说明:副作用部分(setTimeout / game.connect / game.reload / 重连遮罩)及端到端真机断线重连行为难以自动化,需人工联机测试验证。

扩展适配

无。本 PR 仅为防御性检查与优雅降级,未改变任何对外 API 或联机消息协议。

检查清单

  • commit中没有无用信息,和没有具体内容的“bugfix”
  • 我已经进行了充足的测试,且现有的测试都已通过
  • 若我拥有PR标签权限,则已确保为该PR打上标签;若我未拥有PR标签权限且该PR仍需继续提交内容,则已确保为该PR名称打上WIP直到本PR内容全部提交
  • 如果此次PR中添加了新的武将,则我已在character/rank.js中添加对应的武将强度评级,并对双人武将/复姓武将添加name:xxx的参数
  • 如果此次PR中添加了新的语音文件,则我已在lib.translate中加入语音文件的文字台词
  • 如果此次PR涉及到新功能的添加,我已在PR描述中写入详细文档
  • 如果此次PR需要扩展跟进,我已在扩展适配中写入详细文档
  • 如果这个PR解决了一个issue,我在诱因和背景中明确链接到该issue
  • 我保证该PR中没有随意修改换行符等内容,没有制造出大量的Diff
  • 我保证该PR遵循项目中.editorconfigeslint.config.mjsprettier.config.mjs所规定的代码样式,并且已经通过prettier格式化过代码

- game.send() 增加 WebSocket readyState 检查,避免 CLOSING/CLOSED 时
  send 抛 InvalidStateError
- game.connect() 增加 10s 连接超时,避免服务端无响应时无限等待
- 断线自动重连:指数退避 1s→2s→4s→8s→15s,共 5 次,全部失败回退到
  原有刷新行为;用独立 _status.reconnecting 标志驱动续连,修复纯大厅
  场景下重试链中断、重连遮罩卡住的问题
- broadcast() 改为快照迭代,避免 client.send 失败触发 close 移除自身
  导致迭代漏发
- selfclose 取消自动重连并清理 reconnect_info,防止退出后误重连
- 重连决策逻辑抽离为纯函数 decideReconnect (@/util/reconnect.js) 并
  新增 vitest 单测(11 用例)
@rintim rintim changed the title feat(online): 增强客户端连接鲁棒性 feat(online): 增强客户端连接稳定性 May 31, 2026
@rintim

rintim commented May 31, 2026

Copy link
Copy Markdown
Member

不要鲁棒性(

@1937475624

Copy link
Copy Markdown
Collaborator

这个功能如果没bug我没意见

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants