# 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：首次连接的客户端被标记为 participant（polite=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)

### 广播与消息路由
- 组内广播：处理器提供 broadcastToGroup，host 向所有 participants 转发，participants 向 host 转发。
- 全局广播：onBroadcast 支持向指定连接组或全局广播消息。
- 聊天消息：onMessage 将 host 的消息广播给 participants，participant 的消息转发给 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 连接，成为 host（polite=false）。
- 步骤 2：客户端 B 连接，成为 participant（polite=true）。
- 步骤 3：B 发送 offer，处理器转发给 A。
- 步骤 4：A 回答 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)