Files
video_socket-server/.qoder/repowiki/zh/content/信令系统/WebSocket 信令处理器.md
2026-05-16 13:24:02 +08:00

18 KiB
Raw Blame History

WebSocket 信令处理器

**本文引用的文件** - [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)

目录

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

简介

本文件面向 WebSocket 信令处理器的技术文档,聚焦于 WebSocket 连接管理、连接组管理主机与参与者、Offer/Answer 协商流程、ICE 候选者传输、私有/公共模式差异、心跳与超时处理以及连接恢复策略。文档同时提供流程图与调试建议,帮助开发者快速理解与扩展系统。

项目结构

  • 服务端入口与启动命令行参数解析、HTTPS/HTTP 服务创建、WebSocket 信令服务器启动。
  • WebSocket 信令层:连接接入、消息分发、心跳与超时、广播与点对点路由。
  • 数据模型Offer/Answer/Candidate 的封装,便于序列化与传输。
  • HTTP 信令层(对比参考):提供轮询式信令的实现思路与数据持久化方案。
  • 日志与配置:统一日志级别与输出格式;运行参数与模式配置。
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

图表来源

章节来源

核心组件

  • WebSocket 信令服务器:负责连接接入、消息解析与分发、心跳与超时处理。
  • 连接组管理器:维护每个连接组的主机与参与者集合,实现双向路由与广播。
  • 数据模型封装Offer/Answer/Candidate 对象,承载 SDP 与 ICE 候选者元数据。
  • 日志系统:统一日志级别与输出格式,便于调试与监控。
  • HTTP 信令(对比):提供轮询式信令的数据持久化与查询能力,便于非 WebSocket 场景。

章节来源

架构总览

WebSocket 信令处理器采用“连接接入 -> 消息分发 -> 组内路由”的三层结构。连接接入由 websocket.ts 完成,消息分发与路由由 websockethandler.ts 实现,数据模型由 offer.ts/answer.ts/candidate.ts 提供。

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)"

图表来源

详细组件分析

连接管理与状态维护

  • 连接添加:在连接事件中调用处理器的 add为新连接创建空的连接 ID 集合。
  • 连接移除:在关闭事件中调用处理器的 remove清理连接组并广播断开消息。
  • 心跳与超时:处理器内置心跳检测(注释掉的 AddHeartbeat/RemoveHeartbeat通过 ping/pong 维护 lastActivity超时触发断开。
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

图表来源

章节来源

连接组管理算法(主机与参与者)

  • 角色分配:首次 onConnect 的客户端作为 host后续为 participants私有模式下首次连接的 polite=false其余为 true。
  • 组内通信规则:
    • host 发送的 offer/answer/candidate 转发给所有 participants。
    • participants 发送的 offer/answer/candidate 转发给 host。
    • 支持按 participantId 精确路由(私有模式下 host 可定向发送给特定 participant
  • 断开处理host 离开时广播断开并删除连接组participant 离开时通知 host 并从组中移除。
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 : "维护/查询"

图表来源

章节来源

Offer/Answer 协商流程WebSocket 实现)

  • SDP 交换:处理器将客户端发送的 SDP 封装为 Offer/Answer 对象,附加时间戳与 polite 标记。
  • 路由规则:
    • 私有模式host 可按 participantId 精确发送 offer/answer否则广播给所有 participants。
    • 公共模式:若连接组不存在则创建,随后向所有其他客户端广播 offer。
  • 参与者角色participant 发送的 offer/answer 自动路由至 host携带发送者的 participantId 以便 host 识别来源。
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 或广播"

图表来源

章节来源

ICE 候选者传输Candidate

  • 传输机制:与 Offer/Answer 类似,处理器将候选者封装为 Candidate 对象,携带 sdpMid、sdpMLineIndex 与时间戳。
  • 路由逻辑:
    • 私有模式host 可按 participantId 精确发送;否则广播。
    • 公共模式:当前实现未覆盖公共模式下的 Candidate 转发(保留注释的实现位置)。
  • 处理器提供按组广播与按角色路由的能力,便于扩展公共模式下的 Candidate 转发。
flowchart TD
A["收到 candidate 消息"] --> B["封装 Candidate 对象"]
B --> C{"私有模式?"}
C --> |是| D["按 participantId 路由或广播"]
C --> |否| E["当前未实现公共模式转发(保留扩展位)"]
D --> F["发送到目标客户端"]
E --> F

图表来源

章节来源

私有模式 vs 公共模式

  • 私有模式private
    • 连接组隔离:每个 connectionId 对应唯一连接组host 与 participants 明确划分。
    • 精确路由:支持按 participantId 精确发送 offer/answer/candidate。
    • 断开影响host 离开导致房间解散并广播断开participant 离开仅通知 host。
  • 公共模式public
    • 广播为主offer 会广播给所有其他客户端answer/candidate 当前未实现广播。
    • 角色默认为 participant首次连接的客户端被标记为 participantpolite=true
graph LR
Private["私有模式"] --> P1["连接组隔离"]
Private --> P2["按 participantId 精确路由"]
Private --> P3["host 离开即解散房间"]
Public["公共模式"] --> U1["offer 广播给其他客户端"]
Public --> U2["answer/candidate 当前未广播(保留扩展)"]
Public --> U3["默认 participant 角色"]

图表来源

章节来源

心跳检测、超时与连接恢复

  • 心跳机制:处理器预留心跳检测(注释掉),通过 ping/pong 维护 lastActivity定时检查超时并触发断开。
  • 超时策略:当前实现注释掉,未在运行时启用;建议在生产环境启用以提升鲁棒性。
  • 连接恢复:客户端需重新发起 connect 流程,处理器会重新分配角色并重建连接组。
flowchart TD
S["开始计时"] --> T["发送 ping"]
T --> R{"收到 pong?"}
R --> |是| U["更新 lastActivity"]
R --> |否| O{"超时(>阈值)?"}
O --> |是| X["执行断开(onDisconnect)"]
O --> |否| T

图表来源

章节来源

广播与消息路由

  • 组内广播:处理器提供 broadcastToGrouphost 向所有 participants 转发participants 向 host 转发。
  • 全局广播onBroadcast 支持向指定连接组或全局广播消息。
  • 聊天消息onMessage 将 host 的消息广播给 participantsparticipant 的消息转发给 host 并同步给其他 participants。

章节来源

与 HTTP 信令的对比(概念性说明)

  • HTTP 信令通过轮询获取 offer/answer/candidate适合无法使用 WebSocket 的场景。
  • 数据持久化HTTP 处理器将信令消息存储在内存映射中,支持按会话与时间过滤查询。
  • 适用场景:弱网环境、代理限制、或需要与传统系统集成的场景。

章节来源

依赖关系分析

  • 入口依赖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 提供依赖与脚本。
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"]

图表来源

章节来源

性能考量

  • 内存占用:连接组与消息对象均使用 Map/Set 存储,复杂度与连接数线性相关。
  • 路由效率:广播与按角色路由均为 O(n) 遍历,适合中小规模并发。
  • 建议优化:
    • 使用连接池与连接复用,避免频繁创建/销毁。
    • 对广播消息进行去重与合并,降低网络负载。
    • 引入心跳与超时(启用注释的实现)以及时回收无效连接。

故障排查指南

  • 连接无法建立:
    • 检查服务端启动参数与证书配置确认端口与协议HTTP/HTTPS正确。
    • 查看日志输出,定位 onConnect/添加连接阶段的问题。
  • 消息未到达:
    • 确认连接组存在且角色正确;核对 participantId 与 connectionId。
    • 检查私有/公共模式下的路由分支是否符合预期。
  • 心跳与超时:
    • 若启用心跳,确认 ping/pong 循环正常;检查 lastActivity 更新。
    • 未启用心跳时,长时间无活动可能导致连接被断开。
  • 单元测试参考:
    • 使用测试用例验证公有/私有模式下的连接、Offer/Answer、Candidate 路由行为。

章节来源

结论

WebSocket 信令处理器通过清晰的连接组模型与角色路由,实现了灵活的多方通信能力。私有模式强调精确路由与房间隔离,公共模式强调广播与易用性。结合心跳与超时机制,系统具备较好的稳定性与可维护性。未来可在公共模式下完善 Candidate 转发与心跳启用,进一步提升可用性与健壮性。

附录

WebSocket 信令流程示例(私有模式)

  • 步骤 1客户端 A 连接,成为 hostpolite=false
  • 步骤 2客户端 B 连接,成为 participantpolite=true
  • 步骤 3B 发送 offer处理器转发给 A。
  • 步骤 4A 回答 answer处理器按 participantId 转发给 B。
  • 步骤 5双方交换 ICE 候选者,处理器按 participantId 转发。
  • 步骤 6任一方断开处理器广播断开并清理连接组。

章节来源

调试技巧

  • 启用详细日志:通过日志级别控制输出,定位连接、路由与广播问题。
  • 使用测试工具:参考单元测试用例,模拟公有/私有模式下的行为。
  • 关注客户端文档OneByOne 客户端 README 描述了 WebSocket 事件与心跳机制,有助于理解客户端侧的配合。

章节来源