1.重构websocket连接

This commit is contained in:
2026-05-27 10:28:39 +08:00
parent a7c2a29764
commit 8f5ba977a4
12 changed files with 1101 additions and 144 deletions

View File

@@ -764,22 +764,35 @@
- **移动端配置缺口****`POST /api/game/lobbyInit` 当前不下发 WebSocket 地址**H5 需与运维约定同一套 `H5_WEBSOCKET_URL`(打包进前端配置、远程配置中心等),与 HTTP API 基址可不同域。
- **混合内容**:若 H5 页面为 **HTTPS**,浏览器要求 WebSocket 使用 **`wss://`**,否则会被拦截。
- **事件投递依赖 Redis**HTTP 侧业务通过 **`GameWebSocketEventBus`**Redis 列表)将事件投递到 WebSocket 进程Redis 不可用或队列异常时,**除 `admin.live.snapshot` 外**的广播类推送可能收不到。后台若订阅了 `admin.live.snapshot`,服务端有**每秒直连构建快照**的兜底,不依赖队列。
- **鉴权(重要)****当前 `GameWebSocketServer` 在握手阶段不校验** URL Query 中的 `token` / `auth_token` / `user-token` 等;**任何人拿到地址即可建立连接**(与 §1 HTTP 接口必须 `auth-token` + `user-token` 不同。Query 中上述参数为**预留/习惯写法**,便于后续若要在 `onWebSocketConnect` 中实现鉴权再与 HTTP 对齐;**文档中若写「未登录返回 1101」属规划口径非现网行为**。
- **握手鉴权(2026-05 重构后强制)**`GameWebSocketServer::onWebSocketConnect` 通过 `GameWebSocketAuthHelper::authorize` 校验 URL Query。两种合法身份
- **mobileH5/移动端)**:必须同时携带 `auth_token`(同 HTTP `auth-token`+ `user_token`(同 HTTP `user-token`,亦支持 `token` 同义)。校验通过后连接被绑定 `user_id`,分发器仅向其推送本人的 user 级主题。
- **admin后台/运维)**:必须携带 `admin_ws_token`(由后台 `wsConfig` 接口签发,写入 Redis Key `dfw:v1:ws:admin_token:{token}`,默认 TTL 7200s。后台已 `wsConfig` 中把该 token 拼到 `ws_url` 一并返回前端透传即可admin 模式 `user_id=0`,可订阅任意主题并收到**全量** user 级推送(运维联调用)。
- 任一身份不通过 → 服务端发送 `{"event":"ws.error","code":1101,"message":"Authentication failed: ..."}` 并立即 `close`
- **服务端按 user_id 过滤user 级主题)**:以下 topic 的 `data.user_id` 必须 **等于** 当前连接绑定的 `user_id` 才会下发——**`bet.win` / `user.streak` / `wallet.changed` / `bet.accepted` / `auto.spin.progress`**。其它 topic`period.tick` / `period.opened` / `jackpot.hit` / `admin.*`按订阅广播。admin 模式不参与此过滤。
- **心跳超时(服务端主动)**:连接 60s 内无任何上行报文(含 `ping`/`subscribe`)即被 server 主动 `close`,触发客户端走重连流程;避免半关闭的僵尸连接长期持有订阅却不能实际送达推送。
- **独立日志通道 `ws`**`runtime/logs/ws.log`(保留 7 天)。记录维度包含 `publish 入队 / popBatch 异常 / dispatchtopic/candidates/matched/skipped_not_owner/skipped_closed/send_failed/ handshake_ok | denied / subscribe / pong / close idle / send failed` 等。排查"为什么没收到推送"时优先看此文件。
- **订阅才有业务推送**:建连后仅会收到握手首帧(见下)及本连接已订阅主题的消息;不发送 `subscribe` 则收不到 `period.tick` 等(`admin.live.snapshot` 同上,需显式订阅)。
### 7.1 WebSocket 连接与消息
- **连接地址**:见 **§7.0**(环境变量 `H5_WEBSOCKET_URL` 或后台 `wsConfig` 返回的 `ws_url`
- **客户端**:浏览器原生 `WebSocket``ws://` / `wss://`
- **连接时携带参数(可选 / 预留**
- URL Query 可带 `token`(与 HTTP 头 `user-token` 同义习惯)、`auth_token`(与 HTTP 头 `auth-token` 同义习惯)、`device_id``lang` 等,**当前服务端不解析、不校验**;若后续版本实现握手鉴权,以发布说明为准
- 示例(习惯写法):`wss://ws.example.com/ws?token=xxx&auth_token=xxx&device_id=ios_001&lang=zh`
- **连接时必带 Query 参数2026-05 起强制**
- **H5/移动端**`auth_token=<HTTP auth-token>` + `user_token=<HTTP user-token>`(亦支持 `token` 同义)。`device_id``lang` 仍可携带,但服务端不强制
- **后台**`admin_ws_token=<wsConfig 返回的 admin_ws_token>`(后台 `wsConfig` 已直接把它拼到 `ws_url`,前端透传即可)。
- 示例:
- H5`wss://ws.example.com/ws/?auth_token=xxx&user_token=yyy&device_id=ios_001&lang=zh`
- 后台:`wss://ws.example.com/ws/?admin_ws_token=zzz`
- 缺失任一必填字段或 token 失效 → 服务端回 `{"event":"ws.error","code":1101,...}` 后立即关闭连接。
- **连接成功首帧(当前实现)**
- `event``ws.connected`
- `message`:固定文案 `WebSocket connected`(便于联调日志)
- `connection_id`:连接唯一标识(进程内)
- `mode``mobile` | `admin`2026-05 新增;表明本连接的鉴权身份)
- `user_id`int2026-05 新增mobile 模式为真实玩家 idadmin 模式为 0
- `server_time`:服务器时间戳(**秒**int
- `heartbeat_interval`:建议心跳间隔(**秒**,当前实现固定为 `30`
- `idle_timeout`:服务端主动关闭的空闲秒数(**秒**,当前实现固定为 `60`;客户端 `idle_timeout - 心跳间隔` 内必须发出 `ping`,否则会被 server 主动 `close`
- **连接后错误帧(当前实现,非 HTTP 业务码)**
- JSON 无法解析:`event`=`ws.error``message`=`Invalid JSON payload`(无 `code` 或与 HTTP `code` 不同体系)
- 未知 `action``event`=`ws.error``message`=`Unsupported action`,并可能带 `received_action`
@@ -807,8 +820,10 @@
#### 7.1.2 订阅行为说明
- **仅建立连接不会自动下发全部业务消息**;客户端需要发送 `subscribe` 明确订阅主题。
- 成功订阅后服务端返回:`{"event":"ws.subscribed","topics":[...]}`
- 成功订阅后服务端返回:`{"event":"ws.subscribed","topics":[...]}`(已去重、按字典序排序,与提交顺序无关)
- **`subscribe` 覆盖式生效**:每次发送都会**完全替换**该连接的订阅集合(不是累加)。需要追加请把已有列表一并发上来。
- 若未订阅主题,通常只能收到握手首帧(`ws.connected`)和心跳回包(`pong`)。
- **服务端按 user_id 过滤**mobile 模式连接只会收到 `data.user_id == 自己 user_id` 的 user 级主题(见 §7.0 列表admin 模式不过滤,收到全量。**客户端仍应做一次防御性 `user_id` 过滤**,避免后续接口变更带来误处理。
- **不下发** `streak_win_reward` 全表110 档);赔率仅通过 `user.streak` / `wallet.changed` / `bet.accepted``lobbyInit.user_snapshot` 推送**当前登录玩家**本局适用字段。
#### 7.1.2A 连胜赔率与连胜场次WebSocket