# 信令客户端 **本文引用的文件** - [client/src/signaling.js](file://client/src/signaling.js) - [client/test/signaling.test.js](file://client/test/signaling.test.js) - [client/test/mocksignaling.js](file://client/test/mocksignaling.js) - [client/src/logger.js](file://client/src/logger.js) - [src/signaling.ts](file://src/signaling.ts) - [src/class/httphandler.ts](file://src/class/httphandler.ts) - [src/websocket.ts](file://src/websocket.ts) - [src/class/websockethandler.ts](file://src/class/websockethandler.ts) - [src/class/offer.ts](file://src/class/offer.ts) - [src/class/answer.ts](file://src/class/answer.ts) - [src/class/candidate.ts](file://src/class/candidate.ts) - [src/server.ts](file://src/server.ts) - [src/index.ts](file://src/index.ts) - [package.json](file://package.json) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件系统性地阐述信令客户端模块的实现机制与协议设计,覆盖以下主题: - 与服务器的连接建立、消息收发与轮询拉取流程 - 信令协议的消息格式、事件类型与状态同步机制 - WebSocket 与 HTTP 轮询两种信令模式的实现差异与适用场景 - 客户端状态管理(连接状态监控、重连与错误处理) - 实战示例(以“代码片段路径”形式给出,避免直接粘贴源码) ## 项目结构 前端信令客户端位于 client/src/signaling.js,提供两类信令适配器: - HTTP 轮询适配器:基于 fetch 的 GET/PUT/POST/DELETE 接口,周期性拉取消息 - WebSocket 适配器:基于原生 WebSocket,实时推送消息 后端信令服务位于 src/signaling.ts 与 src/class/httphandler.ts(HTTP)及 src/websocket.ts 与 src/class/websockethandler.ts(WebSocket),二者均通过统一的事件模型驱动前端。 ```mermaid graph TB subgraph "前端" FE_HTTP["HTTP 信令客户端
client/src/signaling.js"] FE_WS["WebSocket 信令客户端
client/src/signaling.js"] FE_LOGGER["日志工具
client/src/logger.js"] end subgraph "后端" BE_ROUTER["HTTP 路由
src/signaling.ts"] BE_HTTP_HANDLER["HTTP 处理器
src/class/httphandler.ts"] BE_WS_SERVER["WebSocket 服务器
src/websocket.ts"] BE_WS_HANDLER["WebSocket 处理器
src/class/websockethandler.ts"] end FE_HTTP --> BE_ROUTER FE_WS --> BE_WS_SERVER BE_ROUTER --> BE_HTTP_HANDLER BE_WS_SERVER --> BE_WS_HANDLER FE_HTTP --- FE_LOGGER FE_WS --- FE_LOGGER ``` **图表来源** - [client/src/signaling.js:1-292](file://client/src/signaling.js#L1-L292) - [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25) - [src/class/httphandler.ts:1-800](file://src/class/httphandler.ts#L1-L800) - [src/websocket.ts:1-118](file://src/websocket.ts#L1-L118) - [src/class/websockethandler.ts:1-479](file://src/class/websockethandler.ts#L1-L479) **章节来源** - [client/src/signaling.js:1-292](file://client/src/signaling.js#L1-L292) - [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25) - [src/server.ts:1-90](file://src/server.ts#L1-L90) - [src/index.ts:1-109](file://src/index.ts#L1-L109) ## 核心组件 - HTTP 信令客户端(Signaling 类) - 负责会话创建/删除、连接建立/断开、offer/answer/candidate/on-message 的发送与轮询接收 - 通过自定义事件分发消息(connect/disconnect/offer/answer/candidate/on-message) - WebSocket 信令客户端(WebSocketSignaling 类) - 负责 WebSocket 连接生命周期、消息发送与事件分发 - 支持 participant-joined/participant-left/broadcast 等扩展事件 - 日志工具(Logger) - 提供启用/禁用与多种级别输出,便于调试 **章节来源** - [client/src/signaling.js:3-150](file://client/src/signaling.js#L3-L150) - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [client/src/logger.js:1-30](file://client/src/logger.js#L1-L30) ## 架构总览 信令客户端与服务端通过两种模式协作: - HTTP 轮询:客户端周期性 GET /signaling 拉取消息;服务端按会话聚合 offer/answer/candidate/connect/disconnect 并按时间戳排序返回 - WebSocket:客户端直连 WebSocket,服务端按连接组(host/participants)进行消息路由与广播 ```mermaid sequenceDiagram participant C as "信令客户端" participant S as "HTTP 服务端" participant H as "HTTP 处理器" C->>S : PUT /signaling (创建会话) S->>H : createSession() H-->>S : { sessionId } S-->>C : { sessionId } C->>S : PUT /signaling/connection (建立连接) S->>H : createConnection() H-->>S : { connectionId, polite } S-->>C : { connect ... } loop 轮询 C->>S : GET /signaling?fromtime=... S->>H : getAll(fromtime) H-->>S : { messages[], datetime } S-->>C : { messages[], datetime } C-->>C : 分发事件 (offer/answer/candidate/disconnect) end C->>S : DELETE /signaling (删除会话) S->>H : deleteSession() H-->>S : 200 S-->>C : 200 ``` **图表来源** - [client/src/signaling.js:30-97](file://client/src/signaling.js#L30-L97) - [src/signaling.ts:15-18](file://src/signaling.ts#L15-L18) - [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641) **章节来源** - [client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149) - [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641) ## 详细组件分析 ### HTTP 信令客户端(Signaling 类) - 会话管理 - start():若未获取 sessionId,循环 PUT /signaling 创建会话,成功后进入轮询 - stop():DELETE /signaling 清理会话并重置 sessionId - 连接管理 - createConnection()/deleteConnection():PUT/DELETE /signaling/connection - 媒体协商 - sendOffer()/sendAnswer()/sendCandidate():POST /signaling/offer|answer|candidate - 通用消息 - sendMessage():POST /signaling/on-message - 消息拉取 - loopGetAll():GET /signaling?fromtime=lastTime,解析 messages 数组并分发事件 - 事件分发 - disconnect/offer/answer/candidate/on-message 等事件通过 CustomEvent 派发 ```mermaid classDiagram class Signaling { +running boolean +interval number +sessionId string +headers() object +url(method, parameter) string +start() void +stop() void +createConnection(connectionId) Promise +deleteConnection(connectionId) Promise +sendOffer(connectionId, sdp) Promise +sendAnswer(connectionId, sdp) Promise +sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex) Promise +sendMessage(connectionId, message) Promise +getAll(fromTime) Promise -loopGetAll() void } ``` **图表来源** - [client/src/signaling.js:3-150](file://client/src/signaling.js#L3-L150) **章节来源** - [client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149) ### WebSocket 信令客户端(WebSocketSignaling 类) - 连接建立 - 构造函数根据协议选择 ws/wss,监听 open/close/message - start():等待 isWsOpen 为真 - stop():关闭连接并等待 isWsOpen 为假 - 消息发送 - createConnection()/deleteConnection() - sendOffer()/sendAnswer()/sendCandidate() - sendMessage():封装 type:"on-message" 数据 - 消息接收与事件分发 - onmessage:解析 JSON,按 type 分派 connect/disconnect/offer/answer/candidate/on-message/participant-joined/participant-left/broadcast ```mermaid sequenceDiagram participant C as "WebSocket 客户端" participant WS as "WebSocket 服务端" participant WH as "WebSocket 处理器" C->>WS : CONNECT ws : //host WS-->>C : onopen C->>WS : {"type" : "connect","connectionId" : id} WS->>WH : onConnect(ws, id) WH-->>WS : 广播/通知 WS-->>C : {"type" : "connect", ...} C->>WS : {"type" : "offer","from" : id,"data" : {"sdp",...}} WS->>WH : onOffer(ws, data) WH-->>WS : 路由/广播 WS-->>C : {"type" : "offer", ...} C->>WS : {"type" : "on-message", "data" : {...}} WS->>WH : onMessage(ws, data) WH-->>WS : 转发 WS-->>C : {"type" : "on-message", ...} C->>WS : CLOSE WS-->>C : onclose ``` **图表来源** - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [src/websocket.ts:27-115](file://src/websocket.ts#L27-L115) - [src/class/websockethandler.ts:145-338](file://src/class/websockethandler.ts#L145-L338) **章节来源** - [client/src/signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [src/websocket.ts:27-115](file://src/websocket.ts#L27-L115) - [src/class/websockethandler.ts:145-338](file://src/class/websockethandler.ts#L145-L338) ### 信令协议与消息格式 - HTTP 模式 - 会话:PUT /signaling -> { sessionId } - 连接:PUT /signaling/connection -> { connectionId, polite } - 消息:GET /signaling?fromtime=... -> { messages[], datetime } - 消息类型:connect/disconnect/offer/answer/candidate/on-message - offer/answer 字段:connectionId, sdp, polite(可选) - candidate 字段:connectionId, candidate, sdpMLineIndex, sdpMid - WebSocket 模式 - 连接:{"type":"connect","connectionId":id} - 信令:{"type":"offer|answer|candidate","from":id,"data":{...},"participantId":...} - 普通消息:{"type":"on-message","data":{...}} - 扩展事件:participant-joined/participant-left/broadcast ```mermaid flowchart TD A["HTTP 消息聚合"] --> B["按时间戳排序"] B --> C["分发事件:offer/answer/candidate/disconnect"] D["WebSocket 消息路由"] --> E["按连接组(host/participants)转发"] E --> F["广播/定向转发"] ``` **图表来源** - [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641) - [src/class/websockethandler.ts:97-109](file://src/class/websockethandler.ts#L97-L109) **章节来源** - [src/class/httphandler.ts:398-641](file://src/class/httphandler.ts#L398-L641) - [src/class/websockethandler.ts:214-338](file://src/class/websockethandler.ts#L214-L338) ### 状态管理与重连策略 - HTTP 轮询 - start() 中若 sessionId 为空则持续尝试创建会话,间隔由 interval 控制 - loopGetAll() 中每次拉取后更新 lastTimeRequest,避免重复消费 - stop() 显式清理会话 - WebSocket - 构造函数监听 open/close,start() 等待连接就绪 - 建议在 onclose 中实现指数退避重连(可在上层封装) - 错误处理 - on-message 解析失败时记录错误日志 - HTTP 模式下 404/参数缺失等错误需上层捕获并提示 **章节来源** - [client/src/signaling.js:30-97](file://client/src/signaling.js#L30-L97) - [client/src/signaling.js:152-241](file://client/src/signaling.js#L152-L241) - [client/src/logger.js:27-29](file://client/src/logger.js#L27-L29) ### 代码示例(以路径引用代替代码) - 如何监听并处理 on-message 事件 - [client/src/signaling.js:74-83](file://client/src/signaling.js#L74-L83) - 如何发送 offer/answer/candidate - [client/src/signaling.js:117-138](file://client/src/signaling.js#L117-L138) - [client/src/signaling.js:255-279](file://client/src/signaling.js#L255-L279) - 如何启动/停止 HTTP 信令客户端 - [client/src/signaling.js:30-47](file://client/src/signaling.js#L30-L47) - [client/src/signaling.js:93-97](file://client/src/signaling.js#L93-L97) - 如何启动/停止 WebSocket 信令客户端 - [client/src/signaling.js:230-241](file://client/src/signaling.js#L230-L241) - [client/src/signaling.js:236-241](file://client/src/signaling.js#L236-L241) - 如何实现消息队列(建议思路) - 使用内存队列暂存未处理的 on-message,按顺序消费并回调处理函数 - 参考事件分发位置:[client/src/signaling.js:74-83](file://client/src/signaling.js#L74-L83) - 如何调试信令通信 - 启用日志:[client/src/logger.js:3-9](file://client/src/logger.js#L3-L9) - 观察网络请求与响应:[client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149) **章节来源** - [client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149) - [client/src/logger.js:3-9](file://client/src/logger.js#L3-L9) ## 依赖关系分析 - 前端依赖 - client/src/signaling.js 依赖 client/src/logger.js - 测试依赖 client/test/signaling.test.js 与 client/test/mocksignaling.js - 后端依赖 - src/server.ts 注册 /signaling 路由并挂载 HTTP 与 WebSocket 信令 - src/signaling.ts 定义路由与鉴权中间件(会话校验) - HTTP 侧:src/class/httphandler.ts 负责会话/连接/消息持久化与聚合 - WebSocket 侧:src/websocket.ts 与 src/class/websockethandler.ts 负责连接组与消息路由 ```mermaid graph LR P["package.json"] --> FE["client/src/signaling.js"] P --> BE_S["src/server.ts"] BE_S --> BE_R["src/signaling.ts"] BE_R --> BE_H["src/class/httphandler.ts"] BE_S --> BE_W["src/websocket.ts"] BE_W --> BE_WH["src/class/websockethandler.ts"] FE --> LOG["client/src/logger.js"] ``` **图表来源** - [package.json:1-60](file://package.json#L1-L60) - [src/server.ts:14-29](file://src/server.ts#L14-L29) - [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25) - [src/class/httphandler.ts:1-120](file://src/class/httphandler.ts#L1-L120) - [src/websocket.ts:1-21](file://src/websocket.ts#L1-L21) - [src/class/websockethandler.ts:1-66](file://src/class/websockethandler.ts#L1-L66) **章节来源** - [package.json:1-60](file://package.json#L1-L60) - [src/server.ts:14-29](file://src/server.ts#L14-L29) - [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25) ## 性能考量 - HTTP 轮询 - 通过 fromtime 参数避免重复拉取,提升效率 - 合理设置 interval,避免过于频繁导致带宽与 CPU 压力 - WebSocket - 服务端支持心跳检测(注释中可见),可结合客户端 ping/pong 保持长连稳定 - 连接组广播需控制消息规模,避免风暴 - 会话超时 - HTTP 处理器定期清理长时间无请求的会话,降低资源占用 **章节来源** - [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232) - [src/class/websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430) ## 故障排查指南 - 无法获取 sessionId - 确认 start() 已调用且网络可达 - 查看控制台日志与网络面板 - 参考:[client/src/signaling.js:30-47](file://client/src/signaling.js#L30-L47) - on-message 解析失败 - 客户端会记录错误日志,检查消息格式与编码 - 参考:[client/src/signaling.js:74-83](file://client/src/signaling.js#L74-L83) - WebSocket 连接异常 - 检查协议(ws/wss)、主机与端口配置 - 关注 onclose 回调,必要时实现重连 - 参考:[client/src/signaling.js:169-176](file://client/src/signaling.js#L169-L176) - 私有模式下未收到对端 offer/answer - 确认双方已建立相同 connectionId 的连接 - 参考测试用例对私有模式行为的断言 - 参考:[client/test/signaling.test.js:214-434](file://client/test/signaling.test.js#L214-L434) **章节来源** - [client/src/signaling.js:30-97](file://client/src/signaling.js#L30-L97) - [client/src/signaling.js:152-241](file://client/src/signaling.js#L152-L241) - [client/test/signaling.test.js:214-434](file://client/test/signaling.test.js#L214-L434) ## 结论 本信令客户端模块提供了统一的事件接口,兼容 HTTP 轮询与 WebSocket 两种模式。HTTP 模式适合受限网络环境,WebSocket 模式具备更低延迟与更丰富的连接组能力。通过清晰的事件分发与日志工具,开发者可以快速集成并调试信令流程。 ## 附录 - 数据模型(Offer/Answer/Candidate) - Offer:sdp、datetime、polite - Answer:sdp、datetime - Candidate:candidate、sdpMLineIndex、sdpMid、datetime - 参考: - [src/class/offer.ts:1-11](file://src/class/offer.ts#L1-L11) - [src/class/answer.ts:1-8](file://src/class/answer.ts#L1-L8) - [src/class/candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)