-
Notifications
You must be signed in to change notification settings - Fork 9
WebSocket API
nixi-agent edited this page Aug 10, 2026
·
1 revision
本文件面向使用 VRChat 好友监控系统的开发者与运维人员,提供完整的 WebSocket API 文档。内容涵盖:
- WebSocket 连接建立过程、认证机制与连接生命周期管理
- 事件类型定义(friend-online、friend-offline、friend-location、friend-active 等)及数据格式说明
- 连接管理最佳实践:重连策略、心跳保活、错误处理
- 完整示例代码路径与事件处理模式
- 性能优化建议与故障排除方法
本项目围绕 WebSocket 连接管理与事件处理构建,关键模块如下:
- WsManager:负责 WebSocket 连接生命周期、认证、心跳、重连、状态回调与事件分发
- VrchatApiClient:负责 HTTP 认证、Cookie/OTP 流程、获取 WebSocket token
- EventPipeline:将标准化后的事件持久化到数据库,并维护世界名缓存
- FriendStateManager:内存中维护好友在线状态与位置信息
- start-monitor.js:主入口,组装各组件,监听 WS 事件并更新本地状态
- 测试脚本:用于直连或经代理连接验证
graph TB
A["应用/测试脚本"] --> B["WsManager<br/>连接管理"]
B --> C["VrchatApiClient<br/>HTTP认证/Token"]
B --> D["WebSocket 管道<br/>wss://pipeline.vrchat.cloud"]
B --> E["EventPipeline<br/>事件持久化"]
B --> F["FriendStateManager<br/>内存状态"]
A --> G["start-monitor.js<br/>主入口"]
G --> E
G --> F
图表来源
- core/ws-manager.js:29-185
- vrchat-api.js:243-330
- core/event-pipeline.js:33-61
- start-monitor.js:54-98
- WsManager:封装连接、认证、心跳、重连、事件解析与回调;支持直连失败自动回退代理;指数退避重连;认证冷却保护
- VrchatApiClient:实现 Cookie 加载/保存、Basic Auth 登录、OTP 验证、ensureAuth 单飞锁、获取 /auth token
- EventPipeline:按事件类型路由处理,写入数据库,维护世界名缓存
- FriendStateManager:内存 Set/Map 维护在线集合与位置信息,支持批量刷新与变更通知
- start-monitor.js:组合上述组件,WS 事件映射为状态更新,并在重连后全量刷新在线列表
下图展示从认证到事件落库的端到端流程,包括直连与代理回退、心跳保活、重连策略。
sequenceDiagram
participant App as "应用/测试"
participant API as "VrchatApiClient"
participant WS as "WsManager"
participant VRC as "VRChat 服务器"
participant EP as "EventPipeline"
participant DB as "存储(SQLite)"
App->>API : "确保认证 (ensureAuth/withAutoOtp)"
API-->>App : "用户信息/需要OTP信号"
App->>API : "GET /auth 获取token"
API-->>App : "{ok : true, token}"
App->>WS : "start()"
WS->>VRC : "WSS 连接 wss : //pipeline.vrchat.cloud/?auth=token"
alt 直连成功
VRC-->>WS : "open"
WS->>WS : "启动心跳(ping/pong)"
else 直连失败
WS->>VRC : "通过代理重试"
VRC-->>WS : "open"
WS->>WS : "启动心跳"
end
VRC-->>WS : "消息(JSON)"
WS->>WS : "解析标准化事件"
WS->>EP : "process(event)"
EP->>DB : "insertEvent/upsertFriend"
WS-->>App : "onEvent(event)/onStatusChange(status)"
图表来源
- core/ws-manager.js:93-185
- vrchat-api.js:243-330
- core/event-pipeline.js:33-61
- 认证流程
- 优先尝试 Cookie 校验,过期则 Basic Auth 登录
- 若需要邮箱 OTP,抛出 needsOtp 信号,上层可调用 ensureAuthWithAutoOtp 自动获取验证码完成登录
- 认证成功后调用 GET /auth 获取 WebSocket token
- 连接建立
- 构造 URL: wss://pipeline.vrchat.cloud/?auth=token
- 先尝试直连,超时失败后自动切换代理(环境变量优先级:VRC_MONITOR_WS_PROXY > HTTPS_PROXY/HTTP_PROXY > 内置默认)
- 设置 User-Agent、Origin 等请求头,握手超时配置
- 连接状态
- 状态机:idle → connecting → connected → reconnecting → error/disconnected
- 每次重连前刷新认证,避免 AuthExpired
- 认证失败时进入冷却期(限流更长),防止频繁触发限流
flowchart TD
Start(["开始"]) --> CheckAuth["检查认证(Cookie/Basic)"]
CheckAuth --> |需要OTP| AutoOtp["自动获取OTP并验证"]
CheckAuth --> |OK| GetToken["GET /auth 获取token"]
AutoOtp --> GetToken
GetToken --> TryDirect["尝试直连 WSS"]
TryDirect --> |成功| Connected["已连接"]
TryDirect --> |失败| UseProxy["通过代理重试"]
UseProxy --> ProxyConnected{"代理成功?"}
ProxyConnected --> |是| Connected
ProxyConnected --> |否| Reconnect["指数退避重连"]
Reconnect --> GetToken
图表来源
- vrchat-api.js:243-330
- core/ws-manager.js:93-185
- 每 30 秒发送 ping,等待 10 秒 pong;未收到则主动断开并重连
- 心跳定时器在连接成功后启动,关闭/错误时清理
- 心跳异常会触发 _onClose -> 重连调度
sequenceDiagram
participant WS as "WsManager"
participant S as "服务器"
WS->>S : "ping (每30s)"
S-->>WS : "pong"
Note over WS,S : "若10s内未收到pong,terminate()并触发重连"
图表来源
- core/ws-manager.js:258-287
- 指数退避延迟序列:1s→2s→4s→8s→16s→30s→60s(封顶)
- MAX_RECONNECT_ATTEMPTS=0 表示无限重试
- 每次重连前重新认证,避免 token 过期
- 认证失败进入冷却期:普通失败 120s,限流 401 场景 300s
flowchart TD
OnClose["连接关闭"] --> Schedule["计算下次重连间隔"]
Schedule --> Wait["等待延迟"]
Wait --> Reconnect["_connect() 重新认证+连接"]
Reconnect --> Success{"连接成功?"}
Success --> |是| Reset["重置attempt/心跳"]
Success --> |否| Schedule
图表来源
- core/ws-manager.js:318-336
- core/ws-manager.js:310-316
- 事件来源:WS 消息体 JSON,包含 type 与 content(content 可能为字符串嵌套 JSON)
- 标准化字段:type、userId、displayName、location、worldId、instanceId、travelingToLocation、platform、content、raw、receivedAt
- 事件类型与处理:
- friend-online:标记好友上线,记录 location/worldId/platform,写入 events 表
- friend-offline:标记离线,lastSeen/lastOffline 更新
- friend-location:更新位置与世界名,不改变在线状态
- user-location:自身位置变化,仅存事件,解析 worldId
- friend-update:更新 displayName/lastSeen
- friend-active:标记在线(活动心跳类事件)
- friend-add/friend-delete:仅存储事件
- notification/notification-v2:通知类事件,仅存储
- 状态同步:
- 重连后调用 /auth/user/friends?offline=false 全量刷新在线列表
- 内存状态 FriendStateManager 提供 O(1) 查询与批量更新
classDiagram
class EventPipeline {
+process(event)
+getStats()
-_handleOnline(event)
-_handleOffline(event)
-_handleLocation(event)
-_handleUserLocation(event)
-_handleUpdate(event)
-_handleActive(event)
-_handleAdd(event)
-_handleDelete(event)
-_handleNotification(event)
-_storeEvent(event, worldName)
-_resolveWorldName(worldId)
}
class Storage {
+upsertFriend(...)
+insertEvent(...)
+getWorldName(worldId)
+save()
}
EventPipeline --> Storage : "读写"
图表来源
- core/event-pipeline.js:33-217
- 通用结构
- type: 事件类型字符串
- content: 对象或字符串(需二次解析)
- 标准化字段由 ws-manager 提取:userId、displayName、location、worldId、instanceId、travelingToLocation、platform、receivedAt
- 具体事件
- friend-online
- 含义:好友上线
- 关键字段:userId、displayName、location、worldId、platform
- 行为:更新在线状态、记录 lastOnline、写入 events
- friend-offline
- 含义:好友下线
- 关键字段:userId
- 行为:标记离线、记录 lastOffline
- friend-location
- 含义:好友位置变化(含 traveling/private/offline)
- 关键字段:userId、location、worldId、displayName
- 行为:更新位置与世界名,不改变在线状态
- friend-active
- 含义:好友活跃(如心跳/活动)
- 关键字段:userId
- 行为:标记在线、更新 lastSeen
- user-location
- 含义:自身位置变化
- 关键字段:location(解析 worldId)
- 行为:仅存事件,不更新好友状态表
- friend-update
- 含义:好友信息更新(如昵称)
- 关键字段:userId、displayName
- 行为:更新 lastSeen
- friend-add / friend-delete
- 含义:好友关系变更
- 行为:仅存储事件
- notification / notification-v2
- 含义:系统通知(如 boop)
- 行为:仅存储事件
- friend-online
- 重连策略
- 使用指数退避,避免雪崩与限流
- 认证冷却:普通失败 120s,限流 401 场景 300s
- 每次重连前刷新认证,保证 token 有效
- 心跳保活
- 30s ping,10s pong 超时检测
- 超时主动断开,触发重连
- 错误处理
- 捕获网络错误、解析错误、认证错误
- 区分直连失败与代理失败,记录日志便于排查
- 状态观测
- 暴露 getState() 获取 status、attempt、uptime、lastToken 掩码
- onStatusChange 回调上报状态变化
- 直连测试
- 参考:test-ws-direct.mjs
- 步骤:认证 → 获取 token → 直连 WSS → 接收消息 → 超时关闭
- 经代理测试
- 参考:test-websocket.mjs
- 步骤:认证 → 获取 token → 通过代理连接 → 统计事件 → 报告
- 事件处理模式
- 统一解析:ws-manager 将 raw 消息标准化为 event
- 路由处理:event-pipeline 根据 type 分派处理器
- 状态同步:start-monitor 将事件映射到 FriendStateManager
- 持久化:event-pipeline 写入 SQLite,定时 flush
- WsManager 依赖 VrchatApiClient 进行认证与 token 获取
- WsManager 将事件交给 EventPipeline 处理与持久化
- start-monitor 组合 WsManager、EventPipeline、FriendStateManager,实现业务逻辑
- 测试脚本直接依赖 VrchatApiClient 与 WebSocket 库,用于验证连通性
graph LR
VM["WsManager"] --> API["VrchatApiClient"]
VM --> EP["EventPipeline"]
SM["start-monitor"] --> VM
SM --> EP
SM --> FS["FriendStateManager"]
T1["test-ws-direct.mjs"] --> API
T2["test-websocket.mjs"] --> API
图表来源
- core/ws-manager.js:29-185
- core/event-pipeline.js:33-61
- start-monitor.js:54-98
- test-ws-direct.mjs:13-71
- test-websocket.mjs:51-200
- 认证节流
- 认证失败冷却:普通 120s,限流 300s,避免频繁触发限流
- 单飞锁:ensureAuth/ensureAuthWithAutoOtp 并发安全
- 连接优化
- 直连优先,失败回退代理,减少握手失败影响
- 心跳 30s ping,10s pong 超时,及时释放无效连接
- 事件处理
- 事件批量持久化:每 100 条或 5s 定时 flush,降低 I/O 压力
- 世界名缓存:按需查询,避免重复 API 调用
- 内存状态
- FriendStateManager 使用 Set/Map,O(1) 查询与更新
- 重连后全量刷新,保证一致性
- 无法直连
- 现象:直连超时,日志提示“直连超时,尝试代理”
- 处理:确认代理配置(环境变量优先级),检查防火墙/代理可达性
- 参考:core/ws-manager.js:147-168
- 认证失败/限流
- 现象:401 或认证错误,进入冷却期
- 处理:等待冷却结束,检查账号状态与网络;必要时手动触发 re-login
- 参考:core/ws-manager.js:310-316
- 心跳超时
- 现象:无 pong,主动断开并重连
- 处理:检查服务端响应、网络质量;调整心跳参数(如需)
- 参考:core/ws-manager.js:268-273
- 事件解析失败
- 现象:JSON 解析异常,记录错误日志
- 处理:检查原始消息,定位上游数据格式问题
- 参考:core/ws-manager.js:233-235
- 状态不一致
- 现象:重连后内存状态缺失
- 处理:触发全量刷新 /auth/user/friends?offline=false
- 参考:start-monitor.js:81-98
本 WebSocket API 实现了高可用的连接管理、健壮的认证与重连机制、标准化的事件处理与持久化。通过心跳保活、指数退避与认证冷却,系统在复杂网络环境下保持稳定。结合内存状态与数据库持久化,提供了高效的好友监控能力。建议在生产环境中合理配置代理、监控心跳与重连日志,并结合全量刷新保证状态一致性。
- 快速上手
- 直连测试:参考 test-ws-direct.mjs
- 代理测试:参考 test-websocket.mjs
- 主服务集成:参考 start-monitor.js
- 关键常量
- 心跳间隔:30s;心跳超时:10s
- 重连延迟序列:1s→2s→4s→8s→16s→30s→60s
- 认证冷却:普通 120s;限流 300s
- 事件类型速查
- friend-online、friend-offline、friend-location、user-location、friend-update、friend-active、friend-add、friend-delete、notification、notification-v2
- API参考手册
- REST API接口
- WebSocket API
- SSE实时推送接口
- MCP 工具接口