做实时游戏的时候,WebSocket 连接断开是个头疼的问题。用户关浏览器、网络断掉、服务器重启,各种情况都要处理。这篇记录一下实际项目中遇到的断开场景和解决方案。
断开场景分类
1 | ┌─────────────────────────────────────────────────────────────────────┐ |
客户端断开场景分析
场景 1:浏览器正常关闭
1 | ┌─────────────────────────────────────────────────────────────┐ |
服务端处理:
1 | // Node.js + ws 库示例 |
关闭状态码说明:
| 状态码 | 名称 | 含义 |
|---|---|---|
| 1000 | Normal Closure | 正常关闭 |
| 1001 | Going Away | 浏览器关闭/离开页面 |
| 1002 | Protocol Error | 协议错误 |
| 1003 | Unsupported Data | 收到不支持的数据类型 |
| 1005 | No Status | 没有状态码 |
| 1006 | Abnormal Closure | 异常关闭(连接意外断开) |
| 1008 | Policy Violation | 违反策略 |
| 1009 | Message Too Big | 消息太大 |
| 1011 | Server Error | 服务器错误 |
| 1012 | Service Restart | 服务器重启 |
| 1013 | Try Again Later | 稍后重试 |
场景 2:浏览器崩溃/进程被杀
1 | ┌─────────────────────────────────────────────────────────────┐ |
服务端处理:
1 | ws.on('close', (code, reason) => { |
场景 3:客户端断网/断电
1 | ┌─────────────────────────────────────────────────────────────┐ |
关键问题:TCP 假死(Zombie Connection)
TCP 连接在以下情况可能保持”假死”状态:
- 物理网络断开(拔网线、断电)
- 客户端网络切换(WiFi ↔ 4G)
- 客户端进入深度休眠
服务端解决方案:
1 | class ConnectionManager { |
服务端断开场景分析
场景 4:服务器正常重启
1 | // 优雅关闭实现 |
场景 5:服务器崩溃/断电
1 | ┌─────────────────────────────────────────────────────────────┐ |
心跳机制最佳实践
心跳策略对比
| 策略 | 心跳间隔 | 检测时间 | 适用场景 |
|---|---|---|---|
| 短周期 | 5-10秒 | 15-30秒 | 实时游戏、高频交易 |
| 中周期 | 30秒 | 60-90秒 | 普通应用、聊天室 |
| 长周期 | 5分钟 | 10-15分钟 | 低频应用、通知推送 |
完整心跳实现
1 | // 服务端心跳管理器 |
重连策略
指数退避重连
1 | class ReconnectManager { |
写在最后
WebSocket 连接断开处理的一些经验:
区分断开类型:
- 优雅关闭:触发 onClose,可正常清理
- 异常断开:可能触发 onError,需要心跳检测
心跳机制:
- 短周期(5-10秒):实时游戏场景
- 中周期(30秒):普通应用场景
- 长周期(5分钟):低频应用场景
TCP 假死处理:
- 仅靠 TCP 无法检测物理层断开
- 必须通过应用层心跳确认连接状态
重连策略:
- 指数退避避免服务器压力
- 最大重试次数防止无限重连
- 随机抖动避免惊群效应
有问题欢迎留言交流。