修改
This commit is contained in:
377
.qoder/repowiki/zh/content/WebRTC 实现/信令客户端.md
Normal file
377
.qoder/repowiki/zh/content/WebRTC 实现/信令客户端.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# 信令客户端
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [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)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
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 信令客户端<br/>client/src/signaling.js"]
|
||||
FE_WS["WebSocket 信令客户端<br/>client/src/signaling.js"]
|
||||
FE_LOGGER["日志工具<br/>client/src/logger.js"]
|
||||
end
|
||||
subgraph "后端"
|
||||
BE_ROUTER["HTTP 路由<br/>src/signaling.ts"]
|
||||
BE_HTTP_HANDLER["HTTP 处理器<br/>src/class/httphandler.ts"]
|
||||
BE_WS_SERVER["WebSocket 服务器<br/>src/websocket.ts"]
|
||||
BE_WS_HANDLER["WebSocket 处理器<br/>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)
|
||||
Reference in New Issue
Block a user