Skip to content

WebSocket通信

nixi-agent edited this page Aug 10, 2026 · 1 revision

WebSocket通信

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件面向VRChat好友监控系统的WebSocket通信子系统,系统性说明连接建立、维护与重连机制;心跳保活算法与超时策略;事件处理管道(从原始消息到标准化事件对象);各类事件(好友在线/离线、位置更新、世界切换等)的处理逻辑;错误处理与异常恢复;连接状态监控与调试工具使用方法;以及性能优化建议与常见问题排查。

项目结构

WebSocket相关代码主要分布在以下模块:

  • ws-manager:负责WSS连接生命周期管理、认证、心跳、重连、状态回调
  • event-pipeline:将WS事件标准化并持久化到SQLite,同时按类型路由到具体处理器
  • vrchat-api:认证流程、Cookie/OTP管理、HTTP API调用
  • storage:SQLite存储层(事件、好友、世界缓存等)
  • friend-state:内存中的好友在线状态缓存与通知
  • start-monitor:主入口,组装各组件,启动WS并桥接事件到状态管理与持久化
  • test-*:直连/代理测试脚本,用于验证连通性与事件接收
graph TB
subgraph "运行时"
A["WsManager<br/>连接/心跳/重连"] --> B["EventPipeline<br/>标准化+持久化"]
A --> C["FriendStateManager<br/>内存状态"]
A --> D["VrchatApiClient<br/>认证/HTTP"]
B --> E["Storage<br/>SQLite"]
end
subgraph "外部服务"
F["VRChat WSS<br/>pipeline.vrchat.cloud"]
G["VRChat HTTP API<br/>api.vrchat.cloud"]
end
A --- F
D --- G
Loading

图表来源

  • core/ws-manager.js:93-185
  • core/event-pipeline.js:33-60
  • vrchat-api.js:243-330
  • core/storage.js:68-74

核心组件

  • WsManager:封装WSS连接、认证、心跳、指数退避重连、状态变更回调、最近事件日志
  • EventPipeline:统一解析WS消息为标准化事件,按type分派到具体处理器,落库events表
  • VrchatApiClient:Cookie/Basic Auth/OTP登录流程,确保鉴权有效,提供HTTP接口
  • Storage:基于better-sqlite3的本地数据库操作,包含事件、好友、世界缓存等读写
  • FriendStateManager:内存中维护好友在线集合与位置信息,支持批量刷新与监听
  • 主入口start-monitor:装配上述组件,启动WS,桥接事件到状态与持久化,并在连接成功后刷新全量在线状态

架构总览

下图展示从WS消息到持久化的端到端流程,包括认证、心跳、重连与事件分发。

sequenceDiagram
participant S as "服务端"
participant WS as "WsManager"
participant API as "VrchatApiClient"
participant EP as "EventPipeline"
participant DB as "Storage(SQLite)"
participant FS as "FriendStateManager"
Note over WS,API : 连接前确保认证有效
WS->>API : ensureAuth()/ensureAuthWithAutoOtp()
API-->>WS : 成功或需要OTP
WS->>S : GET /auth (获取token)
S-->>WS : {ok : true, token}
WS->>S : wss : //...?auth=token
S-->>WS : 连接建立(open)
WS->>WS : 启动心跳(30s ping, 10s pong超时)
loop 每30秒
WS->>S : ping
S-->>WS : pong
end
S-->>WS : message(JSON)
WS->>WS : 解析为标准化event
WS->>EP : onEvent(event)
EP->>DB : insertEvent(...)
EP->>FS : 可选更新内存状态(start-monitor中)
Loading

图表来源

  • core/ws-manager.js:93-185
  • core/ws-manager.js:197-236
  • core/event-pipeline.js:33-60
  • core/storage.js:68-74
  • vrchat-api.js:243-330

详细组件分析

连接建立与维护

  • 认证前置:每次连接前调用ensureAuth,若需要邮箱OTP则通过ensureAuthWithAutoOtp自动获取验证码完成登录
  • Token获取:GET /auth 获取wss连接所需的token
  • 连接策略:优先直连wss://pipeline.vrchat.cloud/?auth=token,失败后回退到代理(环境变量优先级:VRC_MONITOR_WS_PROXY > HTTPS_PROXY/HTTP_PROXY > 内置默认)
  • 握手与超时:直连握手超时6秒,代理模式15秒;open后启动心跳
  • 心跳保活:每30秒ping一次,等待pong最多10秒;未收到pong则主动断开并重试
  • 状态回调:连接/断开/重连时触发onStatusChange,便于上层UI或日志记录
flowchart TD
Start(["开始"]) --> Auth["确保认证有效<br/>ensureAuth/ensureAuthWithAutoOtp"]
Auth --> Token["获取WS Token<br/>GET /auth"]
Token --> TryDirect{"直连成功?"}
TryDirect -- 是 --> Open["open事件"]
TryDirect -- 否 --> Proxy["使用代理重试"]
Proxy --> Open
Open --> Heartbeat["启动心跳<br/>30s ping / 10s pong超时"]
Heartbeat --> Running["运行中"]
Running --> Close{"close/error"}
Close --> Reconnect["指数退避重连<br/>1→2→4→8→16→30→60s"]
Reconnect --> Auth
Loading

图表来源

  • core/ws-manager.js:93-185
  • core/ws-manager.js:258-287
  • core/ws-manager.js:318-336

心跳保活与超时处理

  • 周期:30秒发送一次ping
  • 超时:每次ping后设置10秒定时器,若未收到pong则主动terminate连接
  • 清理:关闭/断开时清除心跳与重连定时器,避免资源泄漏
  • 冷却:认证失败(尤其是限流)会进入冷却期,避免频繁Basic auth触发限流

重连机制

  • 指数退避:延迟序列[1,2,4,8,16,30,60]秒,最大次数可配置(当前为无限重试)
  • 防抖:防止重复调度重连(_reconnectScheduled标志)
  • 认证冷却:根据是否限流设置不同冷却时间,降低服务器压力
  • 连接后刷新:连接成功后在start-monitor中刷新全量在线状态,保证一致性

事件处理管道

  • 标准化:ws-manager将原始JSON消息解析为统一event对象(包含type、userId、displayName、location、worldId、instanceId、platform、content、receivedAt等)
  • 路由:event-pipeline根据type分派到对应处理器(friend-online/offline/location/update/active/add/delete/notification等)
  • 持久化:所有事件最终写入events表,附带worldName(如可解析)与source=websocket
  • 世界名解析:优先查本地world_cache,否则返回空字符串(由外部API按需填充)
flowchart TD
Msg["WS原始消息"] --> Parse["解析JSON/嵌套content"]
Parse --> Normalize["标准化为event对象"]
Normalize --> Route{"按type路由"}
Route --> |friend-online| H1["_handleOnline"]
Route --> |friend-offline| H2["_handleOffline"]
Route --> |friend-location| H3["_handleLocation"]
Route --> |user-location| H4["_handleUserLocation"]
Route --> |friend-update| H5["_handleUpdate"]
Route --> |friend-active| H6["_handleActive"]
Route --> |friend-add| H7["_handleAdd"]
Route --> |friend-delete| H8["_handleDelete"]
Route --> |notification*| H9["_handleNotification"]
Route --> |其他| Store["_storeEvent"]
H1 --> Store
H2 --> Store
H3 --> Store
H4 --> Store
H5 --> Store
H6 --> Store
H7 --> Store
H8 --> Store
H9 --> Store
Store --> DB["写入events表"]
Loading

图表来源

  • core/ws-manager.js:197-236
  • core/event-pipeline.js:33-60
  • core/event-pipeline.js:70-180
  • core/event-pipeline.js:184-210

事件类型处理逻辑

  • friend-online:更新好友在线状态、位置与世界ID,落库事件
  • friend-offline:标记离线,更新last_offline
  • friend-location:更新位置与世界ID,不写回世界名缓存以避免陈旧名称刷新
  • user-location:解析location字符串提取worldId,仅存事件(不更新好友状态表)
  • friend-update/friend-active:更新最后可见时间与在线状态
  • friend-add/friend-delete:仅记录事件
  • notification/notification-v2:仅记录事件

错误处理与异常恢复

  • 认证失败:设置冷却时间,避免高频Basic auth导致限流
  • 连接失败:捕获异常并调度重连
  • 心跳超时:主动断开连接,触发重连
  • 消息解析失败:记录错误但不中断管道
  • 重连保护:防止重复调度,达到最大次数(当前为无限)时置error状态

连接状态监控与调试

  • 状态查询:getState()返回status、attempt、connectedAt、disconnectedAt、uptime、lastToken(脱敏)
  • 事件日志:维护最近100条原始消息摘要,便于定位问题
  • 测试脚本:
    • test-websocket.mjs:通过代理连接,统计事件数量与样例
    • test-ws-direct.mjs:直连测试,快速验证连通性
  • 主入口集成:start-monitor在连接成功后刷新全量在线状态,并输出状态日志

依赖关系分析

  • WsManager依赖VrchatApiClient进行认证与获取WS token
  • EventPipeline依赖Storage进行事件持久化,并可选择性地与FriendStateManager交互(在主入口中)
  • Storage使用better-sqlite3实现高性能、崩溃安全的本地存储
  • 主入口start-monitor协调各组件,定义事件到状态更新的映射
graph LR
WS["WsManager"] --> API["VrchatApiClient"]
WS --> EP["EventPipeline"]
EP --> DB["Storage(SQLite)"]
EP -.可选.-> FS["FriendStateManager"]
SM["start-monitor"] --> WS
SM --> EP
SM --> FS
Loading

图表来源

  • core/ws-manager.js:30-52
  • core/event-pipeline.js:6-15
  • core/storage.js:17-36
  • core/friend-state.js:6-11
  • start-monitor.js:15-22

性能考量

  • 心跳间隔与超时:30秒ping、10秒pong超时,平衡保活与网络开销
  • 重连退避:指数退避减少瞬时重试风暴,提升稳定性
  • 事件批处理:EventPipeline每100个事件触发一次持久化,降低IO频率
  • 定时flush:每5秒检查是否需要保存,避免长时间内存积压
  • 数据库WAL:better-sqlite3启用WAL模式,提高并发读与崩溃安全
  • 世界名缓存:避免频繁API查询,仅在必要时解析
  • 内存状态:FriendStateManager使用Set/Map实现O(1)查询,减少DB压力

故障排查指南

  • 无法连接
    • 检查直连是否可用,必要时启用代理(环境变量配置)
    • 查看握手超时与错误日志
  • 频繁断线
    • 检查心跳是否收到pong,确认网络质量
    • 观察重连日志与冷却时间
  • 认证失败
    • 确认Cookie是否有效,必要时重新登录
    • 如需OTP,检查自动获取流程是否成功
  • 事件缺失
    • 确认WS已连接且onMessage正常触发
    • 检查EventPipeline路由与存储是否正常
  • 性能问题
    • 调整心跳间隔/超时、重连退避策略
    • 评估事件批处理阈值与数据库WAL配置

结论

该WebSocket通信子系统通过健壮的认证、心跳与指数退避重连机制,保障了与VRChat长连接的稳定性;事件处理管道实现了从原始消息到标准化事件的转换与持久化,支持多种事件类型的差异化处理;结合内存状态与本地数据库,提供了高效的状态查询与分析能力。配合测试脚本与状态监控,便于日常运维与问题定位。

附录

  • 环境变量
    • VRC_MONITOR_WS_PROXY:优先使用的WS代理地址
    • HTTPS_PROXY/HTTP_PROXY:备用代理
  • 关键常量
    • 心跳间隔:30秒
    • 心跳超时:10秒
    • 重连延迟序列:1,2,4,8,16,30,60秒
  • 参考脚本
    • test-websocket.mjs:代理连接测试
    • test-ws-direct.mjs:直连快速测试

Clone this wiki locally