Files
video_socket-server/.qoder/repowiki/zh/content/信令系统/WebSocket 信令处理器.md

404 lines
18 KiB
Markdown
Raw Permalink Normal View History

2026-05-16 13:24:02 +08:00
# WebSocket 信令处理器
<cite>
**本文引用的文件**
- [websocket.ts](file://src/websocket.ts)
- [websockethandler.ts](file://src/class/websockethandler.ts)
- [offer.ts](file://src/class/offer.ts)
- [answer.ts](file://src/class/answer.ts)
- [candidate.ts](file://src/class/candidate.ts)
- [index.ts](file://src/index.ts)
- [server.ts](file://src/server.ts)
- [signaling.ts](file://src/signaling.ts)
- [httphandler.ts](file://src/class/httphandler.ts)
- [log.ts](file://src/log.ts)
- [options.ts](file://src/class/options.ts)
- [package.json](file://package.json)
- [websockethandler.test.ts](file://test/websockethandler.test.ts)
- [README.md](file://client/public/onebyone/README.md)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向 WebSocket 信令处理器的技术文档,聚焦于 WebSocket 连接管理、连接组管理主机与参与者、Offer/Answer 协商流程、ICE 候选者传输、私有/公共模式差异、心跳与超时处理以及连接恢复策略。文档同时提供流程图与调试建议,帮助开发者快速理解与扩展系统。
## 项目结构
- 服务端入口与启动命令行参数解析、HTTPS/HTTP 服务创建、WebSocket 信令服务器启动。
- WebSocket 信令层:连接接入、消息分发、心跳与超时、广播与点对点路由。
- 数据模型Offer/Answer/Candidate 的封装,便于序列化与传输。
- HTTP 信令层(对比参考):提供轮询式信令的实现思路与数据持久化方案。
- 日志与配置:统一日志级别与输出格式;运行参数与模式配置。
```mermaid
graph TB
subgraph "服务端"
A["入口: index.ts<br/>解析参数/启动服务"]
B["HTTP 服务: server.ts<br/>静态资源/路由注册"]
C["WebSocket 信令: websocket.ts<br/>连接接入/消息分发"]
D["处理器: websockethandler.ts<br/>连接组/路由/心跳"]
E["数据模型: offer.ts/answer.ts/candidate.ts"]
F["HTTP 信令: signaling.ts/httphandler.ts<br/>轮询式信令(对比)"]
end
A --> B
B --> C
C --> D
D --> E
B --> F
```
图表来源
- [index.ts:52-91](file://src/index.ts#L52-L91)
- [server.ts:14-42](file://src/server.ts#L14-L42)
- [websocket.ts:6-117](file://src/websocket.ts#L6-L117)
- [websockethandler.ts:1-479](file://src/class/websockethandler.ts#L1-L479)
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
- [signaling.ts:1-25](file://src/signaling.ts#L1-L25)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
章节来源
- [index.ts:13-109](file://src/index.ts#L13-L109)
- [server.ts:14-90](file://src/server.ts#L14-L90)
- [websocket.ts:6-117](file://src/websocket.ts#L6-L117)
## 核心组件
- WebSocket 信令服务器:负责连接接入、消息解析与分发、心跳与超时处理。
- 连接组管理器:维护每个连接组的主机与参与者集合,实现双向路由与广播。
- 数据模型封装Offer/Answer/Candidate 对象,承载 SDP 与 ICE 候选者元数据。
- 日志系统:统一日志级别与输出格式,便于调试与监控。
- HTTP 信令(对比):提供轮询式信令的数据持久化与查询能力,便于非 WebSocket 场景。
章节来源
- [websockethandler.ts:10-479](file://src/class/websockethandler.ts#L10-L479)
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
- [log.ts:1-51](file://src/log.ts#L1-L51)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
## 架构总览
WebSocket 信令处理器采用“连接接入 -> 消息分发 -> 组内路由”的三层结构。连接接入由 websocket.ts 完成,消息分发与路由由 websockethandler.ts 实现,数据模型由 offer.ts/answer.ts/candidate.ts 提供。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant WS as "WebSocket 服务器<br/>websocket.ts"
participant Handler as "处理器<br/>websockethandler.ts"
Client->>WS : "建立连接"
WS->>Handler : "add(ws)"
WS->>Client : "connect 消息(含 role/polite/participantId)"
Client->>WS : "offer/answer/candidate/ping/broadcast 等"
WS->>Handler : "根据 type 分派到 onOffer/onAnswer/onCandidate/onBroadcast"
Handler->>Handler : "根据 isPrivate/连接组/角色路由"
Handler-->>Client : "转发对应消息(可能携带 participantId)"
```
图表来源
- [websocket.ts:27-115](file://src/websocket.ts#L27-L115)
- [websockethandler.ts:63-479](file://src/class/websockethandler.ts#L63-L479)
## 详细组件分析
### 连接管理与状态维护
- 连接添加:在连接事件中调用处理器的 add为新连接创建空的连接 ID 集合。
- 连接移除:在关闭事件中调用处理器的 remove清理连接组并广播断开消息。
- 心跳与超时:处理器内置心跳检测(注释掉的 AddHeartbeat/RemoveHeartbeat通过 ping/pong 维护 lastActivity超时触发断开。
```mermaid
flowchart TD
Start(["连接事件"]) --> Add["add(ws)<br/>创建连接ID集合"]
Add --> Role["onConnect(ws, connectionId)<br/>分配角色/生成participantId"]
Role --> Group{"是否已有连接组?"}
Group --> |否| Host["创建连接组(host=ws)"]
Group --> |是| Join["加入现有连接组(participants)"]
Host --> Notify["通知客户端 connect(含 role/polite/participantId)"]
Join --> Notify
Notify --> Msg["等待消息: offer/answer/candidate/ping/broadcast"]
Msg --> Ping{"收到 ping?"}
Ping --> |是| Pong["发送 pong 并更新 lastActivity"]
Ping --> |否| Route["按角色与模式路由消息"]
Route --> Close{"连接关闭/超时?"}
Close --> |是| Remove["remove(ws)<br/>清理并广播断开"]
Close --> |否| Msg
```
图表来源
- [websocket.ts:27-115](file://src/websocket.ts#L27-L115)
- [websockethandler.ts:72-206](file://src/class/websockethandler.ts#L72-L206)
- [websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430)
章节来源
- [websocket.ts:27-115](file://src/websocket.ts#L27-L115)
- [websockethandler.ts:72-206](file://src/class/websockethandler.ts#L72-L206)
- [websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430)
### 连接组管理算法(主机与参与者)
- 角色分配:首次 onConnect 的客户端作为 host后续为 participants私有模式下首次连接的 polite=false其余为 true。
- 组内通信规则:
- host 发送的 offer/answer/candidate 转发给所有 participants。
- participants 发送的 offer/answer/candidate 转发给 host。
- 支持按 participantId 精确路由(私有模式下 host 可定向发送给特定 participant
- 断开处理host 离开时广播断开并删除连接组participant 离开时通知 host 并从组中移除。
```mermaid
classDiagram
class ConnectionGroup {
+host : WebSocket
+participants : Set~WebSocket~
}
class Handler {
+connectionGroup : Map~string, ConnectionGroup~
+isPrivate : boolean
+onConnect(ws, connectionId)
+onDisconnect(ws, connectionId)
+onOffer(ws, message)
+onAnswer(ws, message)
+onCandidate(ws, message)
+broadcastToGroup(connectionId, senderWs, message)
}
Handler --> ConnectionGroup : "维护/查询"
```
图表来源
- [websockethandler.ts:27-37](file://src/class/websockethandler.ts#L27-L37)
- [websockethandler.ts:145-206](file://src/class/websockethandler.ts#L145-L206)
- [websockethandler.ts:214-338](file://src/class/websockethandler.ts#L214-L338)
章节来源
- [websockethandler.ts:145-206](file://src/class/websockethandler.ts#L145-L206)
- [websockethandler.ts:214-338](file://src/class/websockethandler.ts#L214-L338)
### Offer/Answer 协商流程WebSocket 实现)
- SDP 交换:处理器将客户端发送的 SDP 封装为 Offer/Answer 对象,附加时间戳与 polite 标记。
- 路由规则:
- 私有模式host 可按 participantId 精确发送 offer/answer否则广播给所有 participants。
- 公共模式:若连接组不存在则创建,随后向所有其他客户端广播 offer。
- 参与者角色participant 发送的 offer/answer 自动路由至 host携带发送者的 participantId 以便 host 识别来源。
```mermaid
sequenceDiagram
participant P1 as "参与者1"
participant P2 as "参与者2"
participant Host as "主机"
participant Handler as "处理器"
P1->>Handler : "onOffer({connectionId,sdp})"
Handler->>Handler : "封装为 Offer 对象"
alt 私有模式
Handler->>P2 : "按 participantId 转发 offer"
Handler->>Host : "转发 offer(含 participantId)"
else 公共模式
Handler->>P2 : "广播 offer"
end
Host->>Handler : "onAnswer({connectionId,sdp,participantId?})"
Handler->>P2 : "按 participantId 转发 answer 或广播"
```
图表来源
- [websockethandler.ts:214-301](file://src/class/websockethandler.ts#L214-L301)
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
章节来源
- [websockethandler.ts:214-301](file://src/class/websockethandler.ts#L214-L301)
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
### ICE 候选者传输Candidate
- 传输机制:与 Offer/Answer 类似,处理器将候选者封装为 Candidate 对象,携带 sdpMid、sdpMLineIndex 与时间戳。
- 路由逻辑:
- 私有模式host 可按 participantId 精确发送;否则广播。
- 公共模式:当前实现未覆盖公共模式下的 Candidate 转发(保留注释的实现位置)。
- 处理器提供按组广播与按角色路由的能力,便于扩展公共模式下的 Candidate 转发。
```mermaid
flowchart TD
A["收到 candidate 消息"] --> B["封装 Candidate 对象"]
B --> C{"私有模式?"}
C --> |是| D["按 participantId 路由或广播"]
C --> |否| E["当前未实现公共模式转发(保留扩展位)"]
D --> F["发送到目标客户端"]
E --> F
```
图表来源
- [websockethandler.ts:309-338](file://src/class/websockethandler.ts#L309-L338)
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
章节来源
- [websockethandler.ts:309-338](file://src/class/websockethandler.ts#L309-L338)
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
### 私有模式 vs 公共模式
- 私有模式private
- 连接组隔离:每个 connectionId 对应唯一连接组host 与 participants 明确划分。
- 精确路由:支持按 participantId 精确发送 offer/answer/candidate。
- 断开影响host 离开导致房间解散并广播断开participant 离开仅通知 host。
- 公共模式public
- 广播为主offer 会广播给所有其他客户端answer/candidate 当前未实现广播。
- 角色默认为 participant首次连接的客户端被标记为 participantpolite=true
```mermaid
graph LR
Private["私有模式"] --> P1["连接组隔离"]
Private --> P2["按 participantId 精确路由"]
Private --> P3["host 离开即解散房间"]
Public["公共模式"] --> U1["offer 广播给其他客户端"]
Public --> U2["answer/candidate 当前未广播(保留扩展)"]
Public --> U3["默认 participant 角色"]
```
图表来源
- [websockethandler.ts:150-260](file://src/class/websockethandler.ts#L150-L260)
- [websockethandler.ts:315-338](file://src/class/websockethandler.ts#L315-L338)
章节来源
- [websockethandler.ts:150-260](file://src/class/websockethandler.ts#L150-L260)
- [websockethandler.ts:315-338](file://src/class/websockethandler.ts#L315-L338)
### 心跳检测、超时与连接恢复
- 心跳机制:处理器预留心跳检测(注释掉),通过 ping/pong 维护 lastActivity定时检查超时并触发断开。
- 超时策略:当前实现注释掉,未在运行时启用;建议在生产环境启用以提升鲁棒性。
- 连接恢复:客户端需重新发起 connect 流程,处理器会重新分配角色并重建连接组。
```mermaid
flowchart TD
S["开始计时"] --> T["发送 ping"]
T --> R{"收到 pong?"}
R --> |是| U["更新 lastActivity"]
R --> |否| O{"超时(>阈值)?"}
O --> |是| X["执行断开(onDisconnect)"]
O --> |否| T
```
图表来源
- [websocket.ts:95-100](file://src/websocket.ts#L95-L100)
- [websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430)
章节来源
- [websocket.ts:95-100](file://src/websocket.ts#L95-L100)
- [websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430)
### 广播与消息路由
- 组内广播:处理器提供 broadcastToGrouphost 向所有 participants 转发participants 向 host 转发。
- 全局广播onBroadcast 支持向指定连接组或全局广播消息。
- 聊天消息onMessage 将 host 的消息广播给 participantsparticipant 的消息转发给 host 并同步给其他 participants。
章节来源
- [websockethandler.ts:97-109](file://src/class/websockethandler.ts#L97-L109)
- [websockethandler.ts:370-402](file://src/class/websockethandler.ts#L370-L402)
- [websockethandler.ts:448-473](file://src/class/websockethandler.ts#L448-L473)
### 与 HTTP 信令的对比(概念性说明)
- HTTP 信令通过轮询获取 offer/answer/candidate适合无法使用 WebSocket 的场景。
- 数据持久化HTTP 处理器将信令消息存储在内存映射中,支持按会话与时间过滤查询。
- 适用场景:弱网环境、代理限制、或需要与传统系统集成的场景。
章节来源
- [signaling.ts:1-25](file://src/signaling.ts#L1-L25)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
## 依赖关系分析
- 入口依赖index.ts 依赖 server.ts 创建 HTTP 服务,再由 server.ts 注册 /signaling 路由与静态资源。
- WebSocket 依赖websocket.ts 依赖 websockethandler.ts 进行连接与消息处理;依赖 log.ts 输出日志。
- 数据模型依赖websockethandler.ts 依赖 offer.ts/answer.ts/candidate.ts 封装 SDP 与候选者。
- 配置依赖index.ts 与 server.ts 读取 options.ts 的运行参数package.json 提供依赖与脚本。
```mermaid
graph TB
Index["index.ts"] --> Server["server.ts"]
Server --> WS["websocket.ts"]
WS --> Handler["websockethandler.ts"]
Handler --> ModelO["offer.ts"]
Handler --> ModelA["answer.ts"]
Handler --> ModelC["candidate.ts"]
WS --> Log["log.ts"]
Index --> Opt["options.ts"]
Server --> Signaling["signaling.ts"]
Signaling --> HttpH["httphandler.ts"]
```
图表来源
- [index.ts:52-91](file://src/index.ts#L52-L91)
- [server.ts:14-42](file://src/server.ts#L14-L42)
- [websocket.ts:6-117](file://src/websocket.ts#L6-L117)
- [websockethandler.ts:1-479](file://src/class/websockethandler.ts#L1-L479)
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
- [log.ts:1-51](file://src/log.ts#L1-L51)
- [options.ts:1-10](file://src/class/options.ts#L1-L10)
- [signaling.ts:1-25](file://src/signaling.ts#L1-L25)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
章节来源
- [index.ts:52-91](file://src/index.ts#L52-L91)
- [server.ts:14-42](file://src/server.ts#L14-L42)
- [websocket.ts:6-117](file://src/websocket.ts#L6-L117)
- [websockethandler.ts:1-479](file://src/class/websockethandler.ts#L1-L479)
- [signaling.ts:1-25](file://src/signaling.ts#L1-L25)
- [httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800)
## 性能考量
- 内存占用:连接组与消息对象均使用 Map/Set 存储,复杂度与连接数线性相关。
- 路由效率:广播与按角色路由均为 O(n) 遍历,适合中小规模并发。
- 建议优化:
- 使用连接池与连接复用,避免频繁创建/销毁。
- 对广播消息进行去重与合并,降低网络负载。
- 引入心跳与超时(启用注释的实现)以及时回收无效连接。
## 故障排查指南
- 连接无法建立:
- 检查服务端启动参数与证书配置确认端口与协议HTTP/HTTPS正确。
- 查看日志输出,定位 onConnect/添加连接阶段的问题。
- 消息未到达:
- 确认连接组存在且角色正确;核对 participantId 与 connectionId。
- 检查私有/公共模式下的路由分支是否符合预期。
- 心跳与超时:
- 若启用心跳,确认 ping/pong 循环正常;检查 lastActivity 更新。
- 未启用心跳时,长时间无活动可能导致连接被断开。
- 单元测试参考:
- 使用测试用例验证公有/私有模式下的连接、Offer/Answer、Candidate 路由行为。
章节来源
- [log.ts:30-50](file://src/log.ts#L30-L50)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
## 结论
WebSocket 信令处理器通过清晰的连接组模型与角色路由,实现了灵活的多方通信能力。私有模式强调精确路由与房间隔离,公共模式强调广播与易用性。结合心跳与超时机制,系统具备较好的稳定性与可维护性。未来可在公共模式下完善 Candidate 转发与心跳启用,进一步提升可用性与健壮性。
## 附录
### WebSocket 信令流程示例(私有模式)
- 步骤 1客户端 A 连接,成为 hostpolite=false
- 步骤 2客户端 B 连接,成为 participantpolite=true
- 步骤 3B 发送 offer处理器转发给 A。
- 步骤 4A 回答 answer处理器按 participantId 转发给 B。
- 步骤 5双方交换 ICE 候选者,处理器按 participantId 转发。
- 步骤 6任一方断开处理器广播断开并清理连接组。
章节来源
- [websockethandler.ts:145-206](file://src/class/websockethandler.ts#L145-L206)
- [websockethandler.ts:214-301](file://src/class/websockethandler.ts#L214-L301)
- [websockethandler.ts:309-338](file://src/class/websockethandler.ts#L309-L338)
### 调试技巧
- 启用详细日志:通过日志级别控制输出,定位连接、路由与广播问题。
- 使用测试工具:参考单元测试用例,模拟公有/私有模式下的行为。
- 关注客户端文档OneByOne 客户端 README 描述了 WebSocket 事件与心跳机制,有助于理解客户端侧的配合。
章节来源
- [log.ts:15-24](file://src/log.ts#L15-L24)
- [websockethandler.test.ts:1-191](file://test/websockethandler.test.ts#L1-L191)
- [README.md:126-136](file://client/public/onebyone/README.md#L126-L136)