# 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 信令层(对比参考):提供轮询式信令的实现思路与数据持久化方案。 - 日志与配置:统一日志级别与输出格式;运行参数与模式配置。 ```mermaid graph TB subgraph "服务端" A["入口: index.ts
解析参数/启动服务"] B["HTTP 服务: server.ts
静态资源/路由注册"] C["WebSocket 信令: websocket.ts
连接接入/消息分发"] D["处理器: websockethandler.ts
连接组/路由/心跳"] E["数据模型: offer.ts/answer.ts/candidate.ts"] F["HTTP 信令: signaling.ts/httphandler.ts
轮询式信令(对比)"] 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 服务器
websocket.ts" participant Handler as "处理器
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)
创建连接ID集合"] Add --> Role["onConnect(ws, connectionId)
分配角色/生成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)
清理并广播断开"] 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)