修改
This commit is contained in:
379
.qoder/repowiki/zh/content/信令系统/HTTP 信令处理器.md
Normal file
379
.qoder/repowiki/zh/content/信令系统/HTTP 信令处理器.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# HTTP 信令处理器
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [src/class/httphandler.ts](file://src/class/httphandler.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/signaling.ts](file://src/signaling.ts)
|
||||
- [src/server.ts](file://src/server.ts)
|
||||
- [src/index.ts](file://src/index.ts)
|
||||
- [client/src/signaling.js](file://client/src/signaling.js)
|
||||
- [test/httphandler.test.ts](file://test/httphandler.test.ts)
|
||||
- [src/log.ts](file://src/log.ts)
|
||||
- [src/class/options.ts](file://src/class/options.ts)
|
||||
- [src/websocket.ts](file://src/websocket.ts)
|
||||
- [src/class/websockethandler.ts](file://src/class/websockethandler.ts)
|
||||
- [src/服务端接口与WebSocket消息类型.md](file://src/服务端接口与WebSocket消息类型.md)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概览](#架构概览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件为 HTTP 信令处理器的综合技术文档,重点阐述基于 HTTP 轮询的信令工作机制与实现细节。HTTP 信令通过一组 RESTful 接口实现会话管理、消息缓存与检索、以及客户端轮询拉取等核心能力。文档同时提供与 WebSocket 信令的对比分析,涵盖适用场景、性能特点、错误处理、超时管理与重试机制,并给出实际使用示例与最佳实践建议。
|
||||
|
||||
## 项目结构
|
||||
该项目采用前后端分离的模块化组织方式,HTTP 信令相关的核心代码位于以下位置:
|
||||
- 服务器端核心处理器:src/class/httphandler.ts
|
||||
- 信令路由定义:src/signaling.ts
|
||||
- 服务器创建与中间件配置:src/server.ts
|
||||
- 应用入口与协议选择:src/index.ts
|
||||
- 客户端 HTTP 信令实现:client/src/signaling.js
|
||||
- 测试用例:test/httphandler.test.ts
|
||||
- 日志与配置:src/log.ts、src/class/options.ts
|
||||
- WebSocket 对比实现:src/websocket.ts、src/class/websockethandler.ts
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "客户端"
|
||||
C_Signaling["Signaling 类<br/>client/src/signaling.js"]
|
||||
end
|
||||
subgraph "服务器端"
|
||||
S_Index["应用入口<br/>src/index.ts"]
|
||||
S_Server["Express 服务器<br/>src/server.ts"]
|
||||
S_Router["HTTP 路由<br/>src/signaling.ts"]
|
||||
S_Handler["HTTP 处理器<br/>src/class/httphandler.ts"]
|
||||
S_WS["WebSocket 信令<br/>src/websocket.ts"]
|
||||
end
|
||||
C_Signaling --> |"HTTP 轮询"| S_Router
|
||||
S_Router --> S_Handler
|
||||
S_Index --> S_Server
|
||||
S_Server --> S_Router
|
||||
S_Index --> S_WS
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/index.ts:52-91](file://src/index.ts#L52-L91)
|
||||
- [src/server.ts:14-42](file://src/server.ts#L14-L42)
|
||||
- [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25)
|
||||
- [src/class/httphandler.ts:1112-1130](file://src/class/httphandler.ts#L1112-L1130)
|
||||
- [client/src/signaling.js:30-91](file://client/src/signaling.js#L30-L91)
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
|
||||
- [src/server.ts:14-90](file://src/server.ts#L14-L90)
|
||||
- [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25)
|
||||
|
||||
## 核心组件
|
||||
- HTTP 信令处理器:负责会话管理、消息缓存与检索、连接配对、超时清理等核心逻辑。
|
||||
- 数据模型类:Offer、Answer、Candidate,封装信令消息的数据结构。
|
||||
- HTTP 路由层:定义 /signaling 下的 REST 接口,统一鉴权中间件与会话校验。
|
||||
- 客户端轮询实现:基于 Fetch API 的轮询机制,按 fromtime 实现增量拉取。
|
||||
- 测试框架:覆盖公共/私有模式下的会话生命周期、消息传递与超时清理。
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:31-120](file://src/class/httphandler.ts#L31-L120)
|
||||
- [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)
|
||||
- [src/signaling.ts:6-24](file://src/signaling.ts#L6-L24)
|
||||
- [client/src/signaling.js:30-91](file://client/src/signaling.js#L30-L91)
|
||||
- [test/httphandler.test.ts:6-310](file://test/httphandler.test.ts#L6-L310)
|
||||
|
||||
## 架构概览
|
||||
HTTP 信令的整体架构围绕“会话-连接-消息”三层结构展开:
|
||||
- 会话(Session):通过 PUT /signaling 创建,携带 Session-Id 头进行后续请求鉴权。
|
||||
- 连接(Connection):在会话内创建/删除,用于标识对端参与者。
|
||||
- 消息(Offer/Answer/Candidate):在连接之间传递,按 fromtime 实现增量拉取。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "HTTP 路由<br/>src/signaling.ts"
|
||||
participant Handler as "HTTP 处理器<br/>src/class/httphandler.ts"
|
||||
Client->>Router : "PUT /signaling"
|
||||
Router->>Handler : "createSession()"
|
||||
Handler-->>Router : "{ sessionId }"
|
||||
Router-->>Client : "返回 sessionId"
|
||||
Client->>Router : "PUT /signaling/connection"
|
||||
Router->>Handler : "createConnection()"
|
||||
Handler-->>Router : "{ connectionId, polite }"
|
||||
Router-->>Client : "返回连接信息"
|
||||
Client->>Router : "GET /signaling?fromtime=..."
|
||||
Router->>Handler : "getAll()/getOffer()/getAnswer()/getCandidate()"
|
||||
Handler-->>Router : "合并后的消息数组"
|
||||
Router-->>Client : "返回增量消息"
|
||||
Client->>Router : "POST /signaling/offer|answer|candidate"
|
||||
Router->>Handler : "postOffer()/postAnswer()/postCandidate()"
|
||||
Handler-->>Router : "200 OK"
|
||||
Router-->>Client : "确认接收"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/signaling.ts:15-22](file://src/signaling.ts#L15-L22)
|
||||
- [src/class/httphandler.ts:664-675](file://src/class/httphandler.ts#L664-L675)
|
||||
- [src/class/httphandler.ts:739-783](file://src/class/httphandler.ts#L739-L783)
|
||||
- [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641)
|
||||
- [src/class/httphandler.ts:855-886](file://src/class/httphandler.ts#L855-L886)
|
||||
- [src/class/httphandler.ts:913-952](file://src/class/httphandler.ts#L913-L952)
|
||||
- [src/class/httphandler.ts:985-998](file://src/class/httphandler.ts#L985-L998)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 会话管理策略
|
||||
- 会话创建:调用 createSession() 生成唯一 sessionId,并初始化会话级映射(连接集合、Offer/Answer/Candidate 缓存、断开连接记录)。
|
||||
- 会话维护:checkSessionId() 中更新 lastRequestedTime,作为超时判断依据;轮询接口 getAll()/getOffer()/getAnswer()/getCandidate() 均先触发超时检查。
|
||||
- 会话销毁:deleteSession() 删除会话及其所有连接;_deleteConnection() 同步清理连接对、消息缓存与断开记录。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["开始"]) --> CreateSession["创建会话<br/>createSession()"]
|
||||
CreateSession --> InitMaps["初始化会话映射"]
|
||||
InitMaps --> Maintain["维护会话<br/>更新 lastRequestedTime"]
|
||||
Maintain --> Poll["轮询接口<br/>getAll/getOffer/getAnswer/getCandidate"]
|
||||
Poll --> TimeoutCheck{"是否超时?"}
|
||||
TimeoutCheck --> |是| DeleteSession["删除会话<br/>deleteSession()"]
|
||||
TimeoutCheck --> |否| Continue["继续处理"]
|
||||
DeleteSession --> End(["结束"])
|
||||
Continue --> End
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/class/httphandler.ts:664-675](file://src/class/httphandler.ts#L664-L675)
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232)
|
||||
- [src/class/httphandler.ts:697-696](file://src/class/httphandler.ts#L697-L696)
|
||||
- [src/class/httphandler.ts:153-194](file://src/class/httphandler.ts#L153-L194)
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232)
|
||||
- [src/class/httphandler.ts:697-696](file://src/class/httphandler.ts#L697-L696)
|
||||
- [src/class/httphandler.ts:153-194](file://src/class/httphandler.ts#L153-L194)
|
||||
|
||||
### 消息缓存机制
|
||||
- Offer 缓存:以 sessionId -> connectionId -> Offer 映射存储,支持按 fromtime 过滤与跨会话(公共模式)广播。
|
||||
- Answer 缓存:以 sessionId -> connectionId -> Answer 映射存储,公共模式下向配对会话广播。
|
||||
- Candidate 缓存:以 sessionId -> connectionId -> Candidate[] 数组存储,按 fromtime 过滤;Answer 到来时同步更新对应 Candidate 的时间戳。
|
||||
- 断开连接记录:以 sessionId -> Disconnection[] 数组存储,支持按 fromtime 过滤。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Offer {
|
||||
+string sdp
|
||||
+number datetime
|
||||
+boolean polite
|
||||
}
|
||||
class Answer {
|
||||
+string sdp
|
||||
+number datetime
|
||||
}
|
||||
class Candidate {
|
||||
+string candidate
|
||||
+number sdpMLineIndex
|
||||
+string sdpMid
|
||||
+number datetime
|
||||
}
|
||||
class Disconnection {
|
||||
+string id
|
||||
+number datetime
|
||||
}
|
||||
class HTTPHandler {
|
||||
+Map~string,Set~string~~ clients
|
||||
+Map~string,number~ lastRequestedTime
|
||||
+Map~string,Map~string,Offer~~ offers
|
||||
+Map~string,Map~string,Answer~~ answers
|
||||
+Map~string,Map~string,Candidate[]~~ candidates
|
||||
+Map~string,Disconnection[]~~ disconnections
|
||||
+Map~string,[string,string]~ connectionPair
|
||||
}
|
||||
HTTPHandler --> Offer : "缓存"
|
||||
HTTPHandler --> Answer : "缓存"
|
||||
HTTPHandler --> Candidate : "缓存"
|
||||
HTTPHandler --> Disconnection : "记录"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/class/httphandler.ts:42-84](file://src/class/httphandler.ts#L42-L84)
|
||||
- [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)
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:58-84](file://src/class/httphandler.ts#L58-L84)
|
||||
- [src/class/httphandler.ts:268-356](file://src/class/httphandler.ts#L268-L356)
|
||||
- [src/class/httphandler.ts:941-951](file://src/class/httphandler.ts#L941-L951)
|
||||
|
||||
### 轮询接口设计与实现
|
||||
- GET /signaling:合并连接、断开、Offer、Answer、Candidate 消息,按 datetime 升序返回;支持 fromtime 过滤。
|
||||
- GET /signaling/offer|answer|candidate:分别返回对应类型消息,支持 fromtime 过滤。
|
||||
- GET /signaling/connection:返回当前会话的连接列表。
|
||||
- PUT /signaling:创建会话,返回 sessionId。
|
||||
- DELETE /signaling:删除会话。
|
||||
- PUT|DELETE /signaling/connection:创建/删除连接。
|
||||
- POST /signaling/offer|answer|candidate:提交信令消息。
|
||||
|
||||
客户端轮询流程:
|
||||
- 首次启动:PUT /signaling 获取 sessionId,随后循环调用 GET /signaling 并解析消息类型分派事件。
|
||||
- 增量拉取:使用上次响应中的 datetime 作为 fromtime,避免重复接收历史消息。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端<br/>client/src/signaling.js"
|
||||
participant Server as "HTTP 服务器<br/>src/server.ts"
|
||||
participant Router as "路由<br/>src/signaling.ts"
|
||||
participant Handler as "处理器<br/>src/class/httphandler.ts"
|
||||
Client->>Server : "PUT /signaling"
|
||||
Server->>Router : "转发请求"
|
||||
Router->>Handler : "createSession()"
|
||||
Handler-->>Router : "{ sessionId }"
|
||||
Router-->>Client : "返回 sessionId"
|
||||
loop 轮询
|
||||
Client->>Server : "GET /signaling?fromtime=..."
|
||||
Server->>Router : "转发请求"
|
||||
Router->>Handler : "getAll()"
|
||||
Handler-->>Router : "messages + datetime"
|
||||
Router-->>Client : "返回增量消息"
|
||||
end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/server.ts:25-26](file://src/server.ts#L25-L26)
|
||||
- [src/signaling.ts:15-16](file://src/signaling.ts#L15-L16)
|
||||
- [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641)
|
||||
- [client/src/signaling.js:30-91](file://client/src/signaling.js#L30-L91)
|
||||
|
||||
**章节来源**
|
||||
- [src/signaling.ts:9-22](file://src/signaling.ts#L9-L22)
|
||||
- [src/class/httphandler.ts:398-407](file://src/class/httphandler.ts#L398-L407)
|
||||
- [src/class/httphandler.ts:440-447](file://src/class/httphandler.ts#L440-L447)
|
||||
- [src/class/httphandler.ts:492-501](file://src/class/httphandler.ts#L492-L501)
|
||||
- [src/class/httphandler.ts:549-558](file://src/class/httphandler.ts#L549-L558)
|
||||
- [src/class/httphandler.ts:615-641](file://src/class/httphandler.ts#L615-L641)
|
||||
- [client/src/signaling.js:147-149](file://client/src/signaling.js#L147-L149)
|
||||
|
||||
### 与 WebSocket 信令的对比分析
|
||||
- 适用场景
|
||||
- HTTP 轮询:适合受限网络环境(NAT/防火墙)、代理服务器较多、无法建立长连接的场景。
|
||||
- WebSocket:适合实时性要求高、连接稳定、浏览器/移动端原生支持良好的场景。
|
||||
- 性能特点
|
||||
- HTTP 轮询:每次请求均为独立 TCP 连接,存在额外握手开销;但可利用 HTTP 缓存与代理优化。
|
||||
- WebSocket:单连接复用,无握手开销,延迟更低,带宽利用率更高。
|
||||
- 一致性与复杂度
|
||||
- HTTP 轮询:需自行实现 fromtime 增量拉取与会话超时管理。
|
||||
- WebSocket:内置连接状态管理,消息有序到达,无需显式超时处理。
|
||||
|
||||
**章节来源**
|
||||
- [src/服务端接口与WebSocket消息类型.md:533-542](file://src/服务端接口与WebSocket消息类型.md#L533-L542)
|
||||
- [src/websocket.ts:15-39](file://src/websocket.ts#L15-L39)
|
||||
- [src/class/websockethandler.ts](file://src/class/websockethandler.ts)
|
||||
|
||||
### 错误处理、超时管理与重试机制
|
||||
- 会话超时:默认 10 秒未请求则自动清理会话;通过 _checkForTimedOutSessions() 触发。
|
||||
- 连接冲突:私有模式下同一 connectionId 仅允许一次配对,重复使用返回 400。
|
||||
- 请求校验:checkSessionId() 未找到会话返回 404;缺少必要字段返回 400。
|
||||
- 客户端重试:客户端轮询间隔默认 1 秒,若未获取到 sessionId 会持续重试直至成功。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["收到请求"] --> B{"会话存在?"}
|
||||
B --> |否| E["返回 404"]
|
||||
B --> |是| C["更新 lastRequestedTime"]
|
||||
C --> D{"是否超时?"}
|
||||
D --> |是| F["清理会话并返回空消息"]
|
||||
D --> |否| G["继续处理请求"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232)
|
||||
- [test/httphandler.test.ts:29-33](file://test/httphandler.test.ts#L29-L33)
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:31-32](file://src/class/httphandler.ts#L31-L32)
|
||||
- [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232)
|
||||
- [test/httphandler.test.ts:29-33](file://test/httphandler.test.ts#L29-L33)
|
||||
- [test/httphandler.test.ts:363-379](file://test/httphandler.test.ts#L363-L379)
|
||||
|
||||
### 实际使用示例与最佳实践
|
||||
- 客户端使用步骤
|
||||
- 初始化 Signaling 实例,设置轮询间隔。
|
||||
- 调用 start() 自动创建会话并进入轮询循环。
|
||||
- 监听 disconnect/offer/answer/candidate 事件处理信令。
|
||||
- 使用 createConnection()/deleteConnection() 管理连接。
|
||||
- 使用 sendOffer()/sendAnswer()/sendCandidate() 发送信令。
|
||||
- 最佳实践
|
||||
- 合理设置轮询间隔(如 1-2 秒),平衡实时性与资源消耗。
|
||||
- 使用 fromtime 实现增量拉取,避免重复处理历史消息。
|
||||
- 在私有模式下确保 connectionId 的唯一性,避免 400 错误。
|
||||
- 在受限网络环境下优先选择 HTTP 轮询,否则推荐 WebSocket。
|
||||
|
||||
**章节来源**
|
||||
- [client/src/signaling.js:30-91](file://client/src/signaling.js#L30-L91)
|
||||
- [client/src/signaling.js:99-138](file://client/src/signaling.js#L99-L138)
|
||||
- [test/httphandler.test.ts:35-153](file://test/httphandler.test.ts#L35-L153)
|
||||
|
||||
## 依赖关系分析
|
||||
- 服务器启动与协议选择:index.ts 根据配置决定启用 HTTP 轮询还是 WebSocket。
|
||||
- 服务器中间件:server.ts 配置 CORS、JSON 解析、静态资源与 Swagger 文档。
|
||||
- 路由与处理器:signaling.ts 注册 HTTP 路由并挂载会话校验中间件,具体逻辑由 httphandler.ts 实现。
|
||||
- 客户端依赖:client/src/signaling.js 通过 Fetch API 与服务器交互。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Index["src/index.ts"] --> Server["src/server.ts"]
|
||||
Server --> Router["src/signaling.ts"]
|
||||
Router --> Handler["src/class/httphandler.ts"]
|
||||
Client["client/src/signaling.js"] --> Router
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/index.ts:75-88](file://src/index.ts#L75-L88)
|
||||
- [src/server.ts:25-41](file://src/server.ts#L25-L41)
|
||||
- [src/signaling.ts:9-22](file://src/signaling.ts#L9-L22)
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:52-91](file://src/index.ts#L52-L91)
|
||||
- [src/server.ts:14-90](file://src/server.ts#L14-L90)
|
||||
- [src/signaling.ts:1-25](file://src/signaling.ts#L1-L25)
|
||||
|
||||
## 性能考虑
|
||||
- 轮询频率:过高的轮询频率会增加服务器负载与网络开销;建议根据业务场景调整至 1-3 秒。
|
||||
- fromtime 增量拉取:减少不必要的数据传输,降低带宽占用。
|
||||
- 超时清理:及时释放闲置会话资源,避免内存泄漏。
|
||||
- 缓存策略:合理控制消息缓存大小,避免长时间运行导致内存增长。
|
||||
|
||||
## 故障排除指南
|
||||
- 404 会话不存在:确认客户端已成功创建会话并正确携带 Session-Id 头。
|
||||
- 400 连接 ID 冲突:私有模式下同一 connectionId 已被使用,需更换或释放。
|
||||
- 超时被清理:若长时间无请求,会话会被自动清理;可通过定期轮询维持会话活性。
|
||||
- 增量消息缺失:检查 fromtime 参数是否正确传递,避免重复消费历史消息。
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:153-194](file://src/class/httphandler.ts#L153-L194)
|
||||
- [test/httphandler.test.ts:29-33](file://test/httphandler.test.ts#L29-L33)
|
||||
- [test/httphandler.test.ts:363-379](file://test/httphandler.test.ts#L363-L379)
|
||||
|
||||
## 结论
|
||||
HTTP 信令处理器通过简洁的 REST 接口实现了完整的会话管理与消息缓存能力,结合客户端轮询机制满足了在受限网络环境下的信令需求。其与 WebSocket 的对比显示了在不同场景下的取舍:WebSocket 更适合高实时性与高并发场景,而 HTTP 轮询则具备更好的兼容性与部署灵活性。配合 fromtime 增量拉取、超时清理与错误处理机制,HTTP 信令可在多数实际应用中稳定运行。
|
||||
|
||||
## 附录
|
||||
- 配置项说明:secure、port、keyfile、certfile、type(websocket/http)、mode(public/private)、logging。
|
||||
- 日志级别:none/error/warn/log/info,默认 info。
|
||||
|
||||
**章节来源**
|
||||
- [src/class/options.ts:1-10](file://src/class/options.ts#L1-L10)
|
||||
- [src/log.ts:1-51](file://src/log.ts#L1-L51)
|
||||
- [src/index.ts:20-41](file://src/index.ts#L20-L41)
|
||||
404
.qoder/repowiki/zh/content/信令系统/WebSocket 信令处理器.md
Normal file
404
.qoder/repowiki/zh/content/信令系统/WebSocket 信令处理器.md
Normal file
@@ -0,0 +1,404 @@
|
||||
# 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)
|
||||
396
.qoder/repowiki/zh/content/信令系统/信令消息对象.md
Normal file
396
.qoder/repowiki/zh/content/信令系统/信令消息对象.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# 信令消息对象
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [offer.ts](file://src/class/offer.ts)
|
||||
- [answer.ts](file://src/class/answer.ts)
|
||||
- [candidate.ts](file://src/class/candidate.ts)
|
||||
- [httphandler.ts](file://src/class/httphandler.ts)
|
||||
- [websockethandler.ts](file://src/class/websockethandler.ts)
|
||||
- [signaling.ts](file://src/signaling.ts)
|
||||
- [websocket.ts](file://src/websocket.ts)
|
||||
- [signaling.js](file://client/src/signaling.js)
|
||||
- [signaling.test.js](file://client/test/signaling.test.js)
|
||||
- [服务端接口与WebSocket消息类型.md](file://src/服务端接口与WebSocket消息类型.md)
|
||||
- [package.json](file://package.json)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件系统性地文档化了 WebRTC 信令消息对象 Offer、Answer 和 Candidate 的数据结构、字段定义与实现细节。重点覆盖:
|
||||
- Offer 类的 SDP 描述封装、时间戳管理与礼貌协商标志(polite)语义
|
||||
- Answer 类的应答消息构建与 SDP 交换流程
|
||||
- Candidate 类的 ICE 候选者序列化与传输机制
|
||||
- 消息对象的创建、验证与序列化方法
|
||||
- JSON 结构规范与字段含义
|
||||
- 实际消息交换示例与调试技巧
|
||||
- 不同通信模式(公共/私有)下的使用差异
|
||||
|
||||
## 项目结构
|
||||
该项目采用前后端分离的 TypeScript/JavaScript 架构,核心信令逻辑位于服务端的 HTTP 与 WebSocket 处理器中,前端提供基于 HTTP 轮询与 WebSocket 的信令客户端。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "客户端"
|
||||
FE_HTTP["HTTP 客户端<br/>signaling.js"]
|
||||
FE_WS["WebSocket 客户端<br/>signaling.js"]
|
||||
end
|
||||
subgraph "服务端"
|
||||
HTTP["HTTP 路由<br/>signaling.ts"]
|
||||
WS["WebSocket 服务器<br/>websocket.ts"]
|
||||
HHTTP["HTTP 处理器<br/>httphandler.ts"]
|
||||
HWS["WebSocket 处理器<br/>websockethandler.ts"]
|
||||
MODELS["消息模型<br/>offer.ts / answer.ts / candidate.ts"]
|
||||
end
|
||||
FE_HTTP --> HTTP
|
||||
FE_WS --> WS
|
||||
HTTP --> HHTTP
|
||||
WS --> HWS
|
||||
HHTTP --> MODELS
|
||||
HWS --> MODELS
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [signaling.ts:1-25](file://src/signaling.ts#L1-L25)
|
||||
- [websocket.ts:1-118](file://src/websocket.ts#L1-L118)
|
||||
- [httphandler.ts:1-120](file://src/class/httphandler.ts#L1-L120)
|
||||
- [websockethandler.ts:1-120](file://src/class/websockethandler.ts#L1-L120)
|
||||
- [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)
|
||||
- [websocket.ts:1-118](file://src/websocket.ts#L1-L118)
|
||||
- [httphandler.ts:1-120](file://src/class/httphandler.ts#L1-L120)
|
||||
- [websockethandler.ts:1-120](file://src/class/websockethandler.ts#L1-L120)
|
||||
- [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)
|
||||
|
||||
## 核心组件
|
||||
- Offer:封装 SDP 描述、时间戳与礼貌协商标志
|
||||
- Answer:封装 SDP 描述与时间戳
|
||||
- Candidate:封装 ICE 候选者、SDP 媒体行索引与媒体 ID、时间戳
|
||||
|
||||
这些类作为纯数据载体,被 HTTP 与 WebSocket 处理器在消息收发时构造与使用。
|
||||
|
||||
章节来源
|
||||
- [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)
|
||||
|
||||
## 架构总览
|
||||
HTTP 与 WebSocket 两条路径共同承载信令消息的收发与存储,处理器负责:
|
||||
- 会话与连接管理
|
||||
- 消息持久化与检索
|
||||
- 消息路由与广播
|
||||
- 通信模式(公共/私有)下的差异化行为
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端(HTTP)"
|
||||
participant R as "HTTP 路由(signaling.ts)"
|
||||
participant H as "HTTP 处理器(httphandler.ts)"
|
||||
participant S as "存储(Maps)"
|
||||
participant WS as "WebSocket 服务器(websocket.ts)"
|
||||
C->>R : POST /signaling/offer
|
||||
R->>H : postOffer(...)
|
||||
H->>S : 存储 Offer 对象
|
||||
H-->>C : 200 OK
|
||||
C->>R : GET /signaling/offer?fromtime=...
|
||||
R->>H : getOffer(...)
|
||||
H->>S : 查询并过滤
|
||||
H-->>C : {offers : [{connectionId,sdp,polite,type,datetime}]}
|
||||
Note over WS,H : WebSocket 路由与处理器同样处理 offer/answer/candidate
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [signaling.ts:20-22](file://src/signaling.ts#L20-L22)
|
||||
- [httphandler.ts:268-318](file://src/class/httphandler.ts#L268-L318)
|
||||
- [websocket.ts:44-114](file://src/websocket.ts#L44-L114)
|
||||
- [websockethandler.ts:214-260](file://src/class/websockethandler.ts#L214-L260)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### Offer 类分析
|
||||
Offer 类用于封装 WebRTC Offer 信令消息的核心数据,包含 SDP 描述、时间戳与礼貌协商标志。
|
||||
|
||||
- 字段定义
|
||||
- sdp: string —— SDP 描述字符串
|
||||
- datetime: number —— 消息创建时间戳(毫秒)
|
||||
- polite: boolean —— 礼貌协商标志,用于避免并发 offer 冲突
|
||||
|
||||
- 构造与使用
|
||||
- HTTP 处理器在收到 offer 后,使用 sdp、当前时间与默认 polite 构造 Offer 对象并持久化
|
||||
- WebSocket 处理器在收到 offer 后,同样构造 Offer 对象并进行路由
|
||||
|
||||
- 礼貌协商(Polite)语义
|
||||
- 在私有模式下,host 的 polite=false,participant 的 polite=true
|
||||
- 在公共模式下,polite 字段通常为 false,由服务端统一构造
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Offer {
|
||||
+string sdp
|
||||
+number datetime
|
||||
+boolean polite
|
||||
+constructor(sdp, datetime, polite)
|
||||
}
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
|
||||
- [websockethandler.ts:214-247](file://src/class/websockethandler.ts#L214-L247)
|
||||
- [httphandler.ts:268-318](file://src/class/httphandler.ts#L268-L318)
|
||||
|
||||
章节来源
|
||||
- [offer.ts:1-11](file://src/class/offer.ts#L1-L11)
|
||||
- [websockethandler.ts:214-247](file://src/class/websockethandler.ts#L214-L247)
|
||||
- [httphandler.ts:268-318](file://src/class/httphandler.ts#L268-L318)
|
||||
|
||||
### Answer 类分析
|
||||
Answer 类用于封装 WebRTC Answer 信令消息的核心数据,包含 SDP 描述与时间戳。
|
||||
|
||||
- 字段定义
|
||||
- sdp: string —— SDP 描述字符串
|
||||
- datetime: number —— 消息创建时间戳(毫秒)
|
||||
|
||||
- 构造与使用
|
||||
- HTTP 处理器在收到 answer 后,使用 sdp、当前时间构造 Answer 对象并持久化
|
||||
- WebSocket 处理器在收到 answer 后,同样构造 Answer 对象并进行路由
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Answer {
|
||||
+string sdp
|
||||
+number datetime
|
||||
+constructor(sdp, datetime)
|
||||
}
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
|
||||
- [websockethandler.ts:268-301](file://src/class/websockethandler.ts#L268-L301)
|
||||
- [httphandler.ts:300-318](file://src/class/httphandler.ts#L300-L318)
|
||||
|
||||
章节来源
|
||||
- [answer.ts:1-8](file://src/class/answer.ts#L1-L8)
|
||||
- [websockethandler.ts:268-301](file://src/class/websockethandler.ts#L268-L301)
|
||||
- [httphandler.ts:300-318](file://src/class/httphandler.ts#L300-L318)
|
||||
|
||||
### Candidate 类分析
|
||||
Candidate 类用于封装 ICE 候选者消息,包含候选者字符串、SDP 媒体行索引与媒体 ID、时间戳。
|
||||
|
||||
- 字段定义
|
||||
- candidate: string —— ICE 候选者描述
|
||||
- sdpMLineIndex: number —— SDP 媒体行索引
|
||||
- sdpMid: string —— SDP 媒体 ID
|
||||
- datetime: number —— 消息创建时间戳(毫秒)
|
||||
|
||||
- 构造与使用
|
||||
- HTTP 处理器在收到 candidate 后,使用 candidate、sdpMLineIndex、sdpMid、当前时间构造 Candidate 对象并持久化
|
||||
- WebSocket 处理器在收到 candidate 后,同样构造 Candidate 对象并进行路由
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Candidate {
|
||||
+string candidate
|
||||
+number sdpMLineIndex
|
||||
+string sdpMid
|
||||
+number datetime
|
||||
+constructor(candidate, sdpMLineIndex, sdpMid, datetime)
|
||||
}
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
|
||||
- [websockethandler.ts:309-338](file://src/class/websockethandler.ts#L309-L338)
|
||||
- [httphandler.ts:320-356](file://src/class/httphandler.ts#L320-L356)
|
||||
|
||||
章节来源
|
||||
- [candidate.ts:1-12](file://src/class/candidate.ts#L1-L12)
|
||||
- [websockethandler.ts:309-338](file://src/class/websockethandler.ts#L309-L338)
|
||||
- [httphandler.ts:320-356](file://src/class/httphandler.ts#L320-L356)
|
||||
|
||||
### 消息对象的创建、验证与序列化
|
||||
|
||||
- 创建
|
||||
- HTTP:客户端通过 POST /signaling/offer、/answer、/candidate 发送 JSON,服务端在处理器中解析并构造对应消息对象
|
||||
- WebSocket:客户端通过发送包含 type 与 data 的 JSON,服务端在 WebSocket 处理器中解析并构造对应消息对象
|
||||
|
||||
- 验证
|
||||
- HTTP:处理器对必要字段进行校验(如 connectionId、sdp、candidate 等),并在缺失时返回错误
|
||||
- WebSocket:处理器对消息结构进行校验,确保 from、to、data 字段存在
|
||||
|
||||
- 序列化
|
||||
- HTTP:消息对象被转换为 JSON 并返回给客户端
|
||||
- WebSocket:消息对象被包装为 { type, from, to, data, participantId } 形式发送
|
||||
|
||||
章节来源
|
||||
- [httphandler.ts:398-558](file://src/class/httphandler.ts#L398-L558)
|
||||
- [websockethandler.ts:192-200](file://src/class/websockethandler.ts#L192-L200)
|
||||
- [signaling.js:117-138](file://client/src/signaling.js#L117-L138)
|
||||
- [signaling.js:255-279](file://client/src/signaling.js#L255-L279)
|
||||
|
||||
### JSON 结构规范与字段含义
|
||||
|
||||
- Offer
|
||||
- 字段:connectionId, sdp, polite, type="offer", datetime
|
||||
- 含义:连接标识、SDP 描述、礼貌协商标志、消息类型、时间戳
|
||||
|
||||
- Answer
|
||||
- 字段:connectionId, sdp, type="answer", datetime
|
||||
- 含义:连接标识、SDP 描述、消息类型、时间戳
|
||||
|
||||
- Candidate
|
||||
- 字段:connectionId, candidate, sdpMLineIndex, sdpMid, type="candidate", datetime
|
||||
- 含义:连接标识、ICE 候选者、SDP 媒体行索引、SDP 媒体 ID、消息类型、时间戳
|
||||
|
||||
- 通用字段
|
||||
- type:消息类型(connect/disconnect/offer/answer/candidate/on-message)
|
||||
- datetime:消息创建时间戳(毫秒)
|
||||
|
||||
章节来源
|
||||
- [服务端接口与WebSocket消息类型.md:141-234](file://src/服务端接口与WebSocket消息类型.md#L141-L234)
|
||||
- [httphandler.ts:398-558](file://src/class/httphandler.ts#L398-L558)
|
||||
|
||||
### 实际消息交换示例与调试技巧
|
||||
|
||||
- HTTP 模式
|
||||
- 客户端通过 PUT /signaling 创建会话,随后通过 PUT /signaling/connection 建立连接
|
||||
- 使用 GET /signaling 获取所有消息,或分别 GET /signaling/offer、/answer、/candidate 拉取增量
|
||||
- 使用 fromtime 参数实现增量拉取,避免重复处理
|
||||
|
||||
- WebSocket 模式
|
||||
- 客户端发送 { type: "connect", connectionId } 建立连接
|
||||
- 发送 { type: "offer"/"answer"/"candidate", from, data } 进行 SDP 交换
|
||||
- 监听服务端推送的 offer/answer/candidate 消息并更新本地 PeerConnection
|
||||
|
||||
- 调试技巧
|
||||
- 使用浏览器开发者工具查看网络面板中的 HTTP 请求与响应
|
||||
- 在 WebSocket 面板观察消息收发,注意 participantId 与 from 字段
|
||||
- 关注服务端日志输出,定位消息路由问题
|
||||
- 使用测试用例参考消息格式与预期行为
|
||||
|
||||
章节来源
|
||||
- [signaling.js:30-150](file://client/src/signaling.js#L30-L150)
|
||||
- [websocket.ts:44-114](file://src/websocket.ts#L44-L114)
|
||||
- [websockethandler.ts:192-200](file://src/class/websockethandler.ts#L192-L200)
|
||||
- [signaling.test.js:89-208](file://client/test/signaling.test.js#L89-L208)
|
||||
|
||||
### 不同通信模式下的使用差异
|
||||
|
||||
- 公共模式(Public)
|
||||
- 所有连接的客户端均可相互通信
|
||||
- offer/answer/candidate 向所有其他客户端广播
|
||||
- 适合点对点直连场景
|
||||
|
||||
- 私有模式(Private)
|
||||
- 一个 connectionId 对应一个房间,包含 1 个 host 与多个 participants
|
||||
- host 为第一个加入者(polite=false),participants 为后续加入者(polite=true)
|
||||
- host 可单播给特定 participant 或广播给所有 participants;participant 仅能发送给 host
|
||||
- host 离开时房间关闭,participants 被断开;participant 离开房间仍保留
|
||||
|
||||
章节来源
|
||||
- [服务端接口与WebSocket消息类型.md:489-505](file://src/服务端接口与WebSocket消息类型.md#L489-L505)
|
||||
- [websockethandler.ts:150-167](file://src/class/websockethandler.ts#L150-L167)
|
||||
- [httphandler.ts:280-289](file://src/class/httphandler.ts#L280-L289)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
O["Offer 类<br/>offer.ts"] --> HH["HTTP 处理器<br/>httphandler.ts"]
|
||||
A["Answer 类<br/>answer.ts"] --> HH
|
||||
C["Candidate 类<br/>candidate.ts"] --> HH
|
||||
HH --> S["会话/连接映射(Maps)"]
|
||||
HH --> WS["WebSocket 服务器<br/>websocket.ts"]
|
||||
HH --> HWS["WebSocket 处理器<br/>websockethandler.ts"]
|
||||
HWS --> O
|
||||
HWS --> A
|
||||
HWS --> C
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [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)
|
||||
- [httphandler.ts:63-77](file://src/class/httphandler.ts#L63-L77)
|
||||
- [websockethandler.ts:5-8](file://src/class/websockethandler.ts#L5-L8)
|
||||
- [websocket.ts:1-118](file://src/websocket.ts#L1-L118)
|
||||
|
||||
章节来源
|
||||
- [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)
|
||||
- [httphandler.ts:63-77](file://src/class/httphandler.ts#L63-L77)
|
||||
- [websockethandler.ts:5-8](file://src/class/websockethandler.ts#L5-L8)
|
||||
- [websocket.ts:1-118](file://src/websocket.ts#L1-L118)
|
||||
|
||||
## 性能考虑
|
||||
- 增量拉取:HTTP GET 支持 fromtime 参数,减少重复消息传输与解析开销
|
||||
- 会话超时:10 秒无请求自动清理会话,避免内存泄漏
|
||||
- 广播策略:公共模式下广播所有消息,私有模式下按需单播/广播,降低网络负载
|
||||
- 心跳检测:可选的心跳机制(ping/pong)用于保持连接活跃,避免被中间设备断开
|
||||
|
||||
[本节为通用建议,不直接分析具体文件]
|
||||
|
||||
## 故障排查指南
|
||||
- HTTP 404:检查会话 ID 是否正确传入请求头 Session-Id
|
||||
- 消息未到达:确认通信模式(公共/私有)与连接配对是否正确
|
||||
- polite 标志异常:检查连接顺序与私有模式下 host/participant 的角色分配
|
||||
- Candidate 缺失:确认 ICE 候选者是否正确序列化,sdpMLineIndex 与 sdpMid 是否匹配
|
||||
- WebSocket 断开:检查心跳机制与网络稳定性,关注服务端日志
|
||||
|
||||
章节来源
|
||||
- [httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [websockethandler.ts:404-430](file://src/class/websockethandler.ts#L404-L430)
|
||||
- [websocket.ts:95-100](file://src/websocket.ts#L95-L100)
|
||||
|
||||
## 结论
|
||||
Offer、Answer 与 Candidate 三类消息对象构成了 WebRTC 信令的核心载体。通过 HTTP 与 WebSocket 双通道,结合公共/私有两种通信模式,系统实现了灵活高效的信令交换能力。理解各字段的语义与消息流转过程,有助于在实际开发中快速定位问题并优化性能。
|
||||
|
||||
[本节为总结性内容,不直接分析具体文件]
|
||||
|
||||
## 附录
|
||||
|
||||
### API 一览(HTTP)
|
||||
- GET /signaling/connection-ids
|
||||
- PUT /signaling
|
||||
- GET /signaling
|
||||
- DELETE /signaling
|
||||
- GET /signaling/connection
|
||||
- PUT /signaling/connection
|
||||
- DELETE /signaling/connection
|
||||
- GET /signaling/offer
|
||||
- POST /signaling/offer
|
||||
- GET /signaling/answer
|
||||
- POST /signaling/answer
|
||||
- GET /signaling/candidate
|
||||
- POST /signaling/candidate
|
||||
|
||||
章节来源
|
||||
- [服务端接口与WebSocket消息类型.md:37-234](file://src/服务端接口与WebSocket消息类型.md#L37-L234)
|
||||
|
||||
### WebSocket 消息类型
|
||||
- connect/disconnect:连接生命周期
|
||||
- offer/answer/candidate:SDP 交换
|
||||
- on-message:通用消息
|
||||
- broadcast:广播消息
|
||||
- call-request:呼叫请求
|
||||
- participant-joined/participant-left:私有模式参与者变更
|
||||
|
||||
章节来源
|
||||
- [服务端接口与WebSocket消息类型.md:262-486](file://src/服务端接口与WebSocket消息类型.md#L262-L486)
|
||||
340
.qoder/repowiki/zh/content/信令系统/信令路由配置.md
Normal file
340
.qoder/repowiki/zh/content/信令系统/信令路由配置.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# 信令路由配置
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [src/index.ts](file://src/index.ts)
|
||||
- [src/server.ts](file://src/server.ts)
|
||||
- [src/signaling.ts](file://src/signaling.ts)
|
||||
- [src/websocket.ts](file://src/websocket.ts)
|
||||
- [src/class/httphandler.ts](file://src/class/httphandler.ts)
|
||||
- [src/class/websockethandler.ts](file://src/class/websockethandler.ts)
|
||||
- [src/class/options.ts](file://src/class/options.ts)
|
||||
- [src/log.ts](file://src/log.ts)
|
||||
- [src/swagger.ts](file://src/swagger.ts)
|
||||
- [package.json](file://package.json)
|
||||
- [test/httphandler.test.ts](file://test/httphandler.test.ts)
|
||||
- [test/websockethandler.test.ts](file://test/websockethandler.test.ts)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件面向信令服务器的路由配置与运行机制,覆盖 WebSocket 与 HTTP 两种信令模式的路由规则、中间件与消息分发逻辑、端点配置项(端口、SSL 证书、CORS)、启动流程与配置参数、最佳实践与性能优化、故障排除与常见问题,以及实际配置示例与部署注意事项。
|
||||
|
||||
## 项目结构
|
||||
- 应用入口与启动:命令行参数解析、HTTP/HTTPS 服务器创建、WebSocket 信令服务启动。
|
||||
- 服务器配置:Express 中间件、CORS、日志、静态资源、Swagger 文档。
|
||||
- 信令路由:HTTP 路由挂载至 /signaling;WebSocket 信令服务独立于 HTTP 服务。
|
||||
- 处理器:HTTP 与 WebSocket 两类处理器分别管理会话、连接、信令消息与广播。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A["命令行入口<br/>src/index.ts"] --> B["Express 应用创建<br/>src/server.ts"]
|
||||
B --> C["HTTP 信令路由挂载<br/>src/signaling.ts"]
|
||||
B --> D["静态资源与上传接口<br/>src/server.ts"]
|
||||
B --> E["Swagger 文档<br/>src/swagger.ts"]
|
||||
A --> F["HTTPS/HTTP 服务器启动<br/>src/index.ts"]
|
||||
F --> G["WebSocket 信令服务<br/>src/websocket.ts"]
|
||||
G --> H["WebSocket 处理器<br/>src/class/websockethandler.ts"]
|
||||
C --> I["HTTP 处理器<br/>src/class/httphandler.ts"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
|
||||
- [src/server.ts:14-89](file://src/server.ts#L14-L89)
|
||||
- [src/signaling.ts:4-24](file://src/signaling.ts#L4-L24)
|
||||
- [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118)
|
||||
- [src/class/websockethandler.ts:63-137](file://src/class/websockethandler.ts#L63-L137)
|
||||
- [src/class/httphandler.ts:110-120](file://src/class/httphandler.ts#L110-L120)
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
|
||||
- [src/server.ts:14-89](file://src/server.ts#L14-L89)
|
||||
|
||||
## 核心组件
|
||||
- 选项解析与启动流程:命令行参数解析、HTTPS/HTTP 选择、端口、日志级别、信令模式与通信模式。
|
||||
- Express 应用与中间件:CORS、日志、静态资源、上传接口、Swagger。
|
||||
- HTTP 信令路由:/signaling 下的 GET/PUT/DELETE/POST 路由,配合会话 ID 中间件。
|
||||
- WebSocket 信令服务:独立于 HTTP 的 ws 服务器,消息类型与广播逻辑。
|
||||
- 处理器:HTTP 处理器(会话/连接/信令存储与查询)、WebSocket 处理器(连接组、广播、心跳)。
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:16-42](file://src/index.ts#L16-L42)
|
||||
- [src/server.ts:18-22](file://src/server.ts#L18-L22)
|
||||
- [src/signaling.ts:6-24](file://src/signaling.ts#L6-L24)
|
||||
- [src/websocket.ts:15-118](file://src/websocket.ts#L15-L118)
|
||||
- [src/class/httphandler.ts:110-120](file://src/class/httphandler.ts#L110-L120)
|
||||
- [src/class/websockethandler.ts:63-137](file://src/class/websockethandler.ts#L63-L137)
|
||||
|
||||
## 架构总览
|
||||
信令服务器同时支持 HTTP 与 WebSocket 两种模式:
|
||||
- HTTP 模式:通过 /signaling 路由进行轮询式信令交换,依赖 session-id 请求头进行会话隔离。
|
||||
- WebSocket 模式:通过独立的 ws 服务器进行实时双向通信,支持广播、1对多连接组、心跳检测与断线通知。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant HTTP as "HTTP 服务器<br/>Express"
|
||||
participant Sig as "HTTP 信令路由<br/>/signaling"
|
||||
participant Hdl as "HTTP 处理器"
|
||||
participant WS as "WebSocket 服务器"
|
||||
participant WSH as "WebSocket 处理器"
|
||||
Client->>HTTP : "GET /config"
|
||||
HTTP-->>Client : "{useWebSocket, startupMode, logging}"
|
||||
Client->>HTTP : "PUT /signaling (创建会话)"
|
||||
HTTP->>Sig : "路由匹配"
|
||||
Sig->>Hdl : "createSession()"
|
||||
Hdl-->>HTTP : "返回 sessionId"
|
||||
HTTP-->>Client : "sessionId"
|
||||
Note over Client,HTTP : "HTTP 轮询模式"
|
||||
Client->>HTTP : "GET /signaling/offer?fromtime=..."
|
||||
HTTP->>Sig : "路由匹配"
|
||||
Sig->>Hdl : "getOffer()"
|
||||
Hdl-->>HTTP : "返回 offer 列表"
|
||||
HTTP-->>Client : "信令数据"
|
||||
Note over Client,WS : "WebSocket 实时模式"
|
||||
Client->>WS : "建立 ws : //... 连接"
|
||||
WS->>WSH : "connection 事件"
|
||||
WSH->>Client : "connect 消息"
|
||||
Client->>WS : "发送 offer/answer/candidate"
|
||||
WS->>WSH : "onOffer/onAnswer/onCandidate"
|
||||
WSH-->>Client : "转发对应信令"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/server.ts:25-29](file://src/server.ts#L25-L29)
|
||||
- [src/signaling.ts:6-24](file://src/signaling.ts#L6-L24)
|
||||
- [src/class/httphandler.ts:661-675](file://src/class/httphandler.ts#L661-L675)
|
||||
- [src/class/httphandler.ts:492-501](file://src/class/httphandler.ts#L492-L501)
|
||||
- [src/websocket.ts:27-115](file://src/websocket.ts#L27-L115)
|
||||
- [src/class/websockethandler.ts:145-168](file://src/class/websockethandler.ts#L145-L168)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### HTTP 信令路由与中间件
|
||||
- 路由挂载:/signaling 下的 GET/PUT/DELETE/POST 路由,统一由 HTTP 处理器实现。
|
||||
- 会话中间件:校验请求头 session-id 是否存在,不存在则返回 404;存在则更新会话最后请求时间。
|
||||
- 会话与连接管理:创建/删除会话、创建/删除连接、查询连接列表、查询所有信令消息。
|
||||
- 信令存储与查询:Offer/Answer/Candidate 存储与按时间过滤查询;公共/私有模式下的路由差异。
|
||||
- CORS 与日志:全局启用 CORS(允许任意源),按配置输出 HTTP 访问日志。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["HTTP 请求进入 /signaling"]) --> CheckSession["检查 session-id 头部"]
|
||||
CheckSession --> SessionExists{"会话存在?"}
|
||||
SessionExists --> |否| Return404["返回 404"]
|
||||
SessionExists --> |是| UpdateTime["更新会话最后请求时间"]
|
||||
UpdateTime --> RouteMatch["匹配具体路由"]
|
||||
RouteMatch --> Handler["调用对应 HTTP 处理器函数"]
|
||||
Handler --> Store["存储/查询信令数据"]
|
||||
Store --> Response["返回 JSON 响应"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/signaling.ts:6-24](file://src/signaling.ts#L6-L24)
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:398-407](file://src/class/httphandler.ts#L398-L407)
|
||||
|
||||
**章节来源**
|
||||
- [src/signaling.ts:6-24](file://src/signaling.ts#L6-L24)
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:398-407](file://src/class/httphandler.ts#L398-L407)
|
||||
|
||||
### WebSocket 信令服务与消息分发
|
||||
- 服务器创建:基于现有 HTTP 服务器创建 ws 服务器,独立于 HTTP。
|
||||
- 连接生命周期:连接建立、消息处理、断开清理。
|
||||
- 消息类型:connect/disconnect/offer/answer/candidate/broadcast/ping/pong/call-request/on-message。
|
||||
- 1对多模式:host 与 participants 的角色与消息路由;广播到组内成员;断线通知。
|
||||
- 心跳检测:定期 ping/pong,超时自动断开。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant WS as "WebSocket 服务器"
|
||||
participant WSH as "WebSocket 处理器"
|
||||
participant C1 as "客户端1(host)"
|
||||
participant C2 as "客户端2(participant)"
|
||||
WS->>WSH : "connection(ws)"
|
||||
WSH->>C1 : "connect {role : host}"
|
||||
WSH->>C2 : "connect {role : participant}"
|
||||
C1->>WS : "offer {connectionId, sdp}"
|
||||
WS->>WSH : "onOffer"
|
||||
WSH-->>C2 : "转发 offer"
|
||||
C2->>WS : "answer {connectionId, sdp}"
|
||||
WS->>WSH : "onAnswer"
|
||||
WSH-->>C1 : "转发 answer"
|
||||
C1->>WS : "broadcast {message}"
|
||||
WS->>WSH : "onBroadcast"
|
||||
WSH-->>C2 : "广播消息"
|
||||
C1->>WS : "disconnect"
|
||||
WS->>WSH : "onDisconnect"
|
||||
WSH-->>C2 : "通知 host 离开"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/websocket.ts:27-115](file://src/websocket.ts#L27-L115)
|
||||
- [src/class/websockethandler.ts:145-301](file://src/class/websockethandler.ts#L145-L301)
|
||||
- [src/class/websockethandler.ts:370-402](file://src/class/websockethandler.ts#L370-L402)
|
||||
|
||||
**章节来源**
|
||||
- [src/websocket.ts:15-118](file://src/websocket.ts#L15-L118)
|
||||
- [src/class/websockethandler.ts:145-301](file://src/class/websockethandler.ts#L145-L301)
|
||||
- [src/class/websockethandler.ts:370-402](file://src/class/websockethandler.ts#L370-L402)
|
||||
|
||||
### 选项与启动流程
|
||||
- 命令行参数:端口、HTTPS 开关、证书路径、信令类型(websocket/http)、通信模式(public/private)、日志级别。
|
||||
- 服务器创建:根据 secure 选择 HTTPS 或 HTTP;读取 key/cert 文件;监听端口并打印访问地址。
|
||||
- 信令类型校验:非 websocket/http 的值将被修正为 websocket 并给出警告。
|
||||
- WebSocket 启动:当 type 为 websocket 时,创建 WSSignaling 实例并传入通信模式。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Parse["解析命令行参数"] --> CreateApp["创建 Express 应用"]
|
||||
CreateApp --> Secure{"secure ?"}
|
||||
Secure --> |是| HTTPS["创建 HTTPS 服务器"]
|
||||
Secure --> |否| HTTP["创建 HTTP 服务器"]
|
||||
HTTPS --> Listen["监听端口并打印地址"]
|
||||
HTTP --> Listen
|
||||
Listen --> Type{"type == http ?"}
|
||||
Type --> |是| LogHttp["记录使用 HTTP 轮询"]
|
||||
Type --> |否| FixType["修正为 websocket 并记录警告"]
|
||||
LogHttp --> Done
|
||||
FixType --> Done
|
||||
Done --> WS{"type == websocket ?"}
|
||||
WS --> |是| StartWS["启动 WebSocket 信令服务"]
|
||||
WS --> |否| End
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/index.ts:20-42](file://src/index.ts#L20-L42)
|
||||
- [src/index.ts:55-82](file://src/index.ts#L55-L82)
|
||||
- [src/index.ts:87-88](file://src/index.ts#L87-L88)
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:20-42](file://src/index.ts#L20-L42)
|
||||
- [src/index.ts:55-82](file://src/index.ts#L55-L82)
|
||||
- [src/index.ts:87-88](file://src/index.ts#L87-L88)
|
||||
|
||||
### CORS 与静态资源
|
||||
- CORS:全局启用,允许任意源访问。
|
||||
- 静态资源:/ 与 /module 路径的静态文件服务。
|
||||
- 上传接口:multer 存储头像到 uploads/avatars,重命名为以 userId 命名的文件并返回访问路径。
|
||||
|
||||
**章节来源**
|
||||
- [src/server.ts:22](file://src/server.ts#L22)
|
||||
- [src/server.ts:27-28](file://src/server.ts#L27-L28)
|
||||
- [src/server.ts:62-86](file://src/server.ts#L62-L86)
|
||||
|
||||
### Swagger 文档
|
||||
- 自动扫描 HTTP 处理器与路由文件,生成 OpenAPI 文档,暴露 /api-docs。
|
||||
- 使用 session-id 作为 API Key 安全方案。
|
||||
|
||||
**章节来源**
|
||||
- [src/swagger.ts:16-65](file://src/swagger.ts#L16-L65)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Index["src/index.ts"] --> Server["src/server.ts"]
|
||||
Index --> WS["src/websocket.ts"]
|
||||
Server --> Sig["src/signaling.ts"]
|
||||
Sig --> Hdl["src/class/httphandler.ts"]
|
||||
WS --> WSH["src/class/websockethandler.ts"]
|
||||
Server --> Swagger["src/swagger.ts"]
|
||||
Server --> Log["src/log.ts"]
|
||||
Index --> Opt["src/class/options.ts"]
|
||||
Package["package.json"] --> Server
|
||||
Package --> WS
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
|
||||
- [src/server.ts:14-89](file://src/server.ts#L14-L89)
|
||||
- [src/signaling.ts:4-24](file://src/signaling.ts#L4-L24)
|
||||
- [src/websocket.ts:6-118](file://src/websocket.ts#L6-L118)
|
||||
- [src/class/httphandler.ts:110-120](file://src/class/httphandler.ts#L110-L120)
|
||||
- [src/class/websockethandler.ts:63-137](file://src/class/websockethandler.ts#L63-L137)
|
||||
- [src/swagger.ts:16-65](file://src/swagger.ts#L16-L65)
|
||||
- [src/log.ts:15-24](file://src/log.ts#L15-L24)
|
||||
- [src/class/options.ts:1-10](file://src/class/options.ts#L1-L10)
|
||||
- [package.json:14-27](file://package.json#L14-L27)
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
|
||||
- [src/server.ts:14-89](file://src/server.ts#L14-L89)
|
||||
- [package.json:14-27](file://package.json#L14-L27)
|
||||
|
||||
## 性能考量
|
||||
- HTTP 轮询 vs WebSocket:WebSocket 适合低延迟实时通信;HTTP 轮询简单但有额外网络开销。
|
||||
- 会话超时:HTTP 处理器按 10 秒超时清理长时间无请求的会话,避免内存泄漏。
|
||||
- 广播与路由:WebSocket 广播需注意组内成员数量,避免大规模广播造成拥塞。
|
||||
- 日志级别:生产环境建议降低日志级别,减少 I/O 压力。
|
||||
- 静态资源与上传:确保上传目录存在并限制文件大小,避免磁盘压力。
|
||||
|
||||
[本节为通用指导,无需列出具体文件来源]
|
||||
|
||||
## 故障排除指南
|
||||
- 会话不存在:HTTP 路由在缺少 session-id 或会话不存在时返回 404。
|
||||
- 会话超时:长时间无请求导致会话被清理,重新创建会话并获取新的 session-id。
|
||||
- 信令类型错误:非 websocket/http 的 type 将被修正为 websocket 并记录警告。
|
||||
- CORS 问题:默认允许任意源,若遇到跨域,请检查客户端与服务器同源策略。
|
||||
- 上传失败:确认 uploads/avatars 目录存在且可写,检查文件名重命名逻辑。
|
||||
|
||||
**章节来源**
|
||||
- [src/class/httphandler.ts:128-145](file://src/class/httphandler.ts#L128-L145)
|
||||
- [src/class/httphandler.ts:218-232](file://src/class/httphandler.ts#L218-L232)
|
||||
- [src/index.ts:78-82](file://src/index.ts#L78-L82)
|
||||
- [src/server.ts:22](file://src/server.ts#L22)
|
||||
- [src/server.ts:44-57](file://src/server.ts#L44-L57)
|
||||
|
||||
## 结论
|
||||
本信令服务器通过 Express 提供 HTTP 轮询与 WebSocket 实时两种信令模式,结合会话与连接管理、CORS、日志与 Swagger 文档,形成完整的信令基础设施。合理配置端口、SSL 证书与通信模式,可满足不同场景需求;通过测试用例可验证 HTTP 与 WebSocket 的行为一致性。
|
||||
|
||||
[本节为总结性内容,无需列出具体文件来源]
|
||||
|
||||
## 附录
|
||||
|
||||
### 信令端点配置选项
|
||||
- 端口:通过命令行参数 -p/--port 指定,默认来自环境变量 PORT。
|
||||
- SSL 证书:通过 -s/--secure 开启 HTTPS,-k/--keyfile 与 -c/--certfile 指定密钥与证书路径。
|
||||
- 信令类型:-t/--type 选择 websocket 或 http。
|
||||
- 通信模式:-m/--mode 选择 public 或 private。
|
||||
- 日志级别:-l/--logging 选择 combined/dev/short/tiny 或 none。
|
||||
|
||||
**章节来源**
|
||||
- [src/index.ts:20-29](file://src/index.ts#L20-L29)
|
||||
- [package.json:9-10](file://package.json#L9-L10)
|
||||
|
||||
### 启动流程与配置参数
|
||||
- 启动命令示例:参考 package.json 中的 start/dev 脚本,包含端口、模式、证书等参数。
|
||||
- 服务器启动:根据 secure 决定 HTTPS/HTTP;打印访问地址;根据 type 启动 WebSocket 或记录 HTTP 轮询模式。
|
||||
|
||||
**章节来源**
|
||||
- [package.json:9-10](file://package.json#L9-L10)
|
||||
- [src/index.ts:55-82](file://src/index.ts#L55-L82)
|
||||
|
||||
### 路由配置最佳实践
|
||||
- 优先使用 WebSocket:实时性要求高时采用 WebSocket,HTTP 轮询仅用于兼容性或受限网络。
|
||||
- 明确通信模式:public 模式下跨会话广播;private 模式下严格按连接组路由。
|
||||
- 控制日志级别:生产环境避免高频日志,必要时仅保留 warn/info。
|
||||
- CORS 与静态资源:生产环境建议限制 CORS 源,静态资源开启缓存策略。
|
||||
|
||||
[本节为通用指导,无需列出具体文件来源]
|
||||
|
||||
### 实际配置示例与部署注意事项
|
||||
- 示例:使用 HTTPS,端口 8080,private 模式,WebSocket 信令。
|
||||
- 部署:确保 server.key 与 server.cert 存在;防火墙开放端口;反向代理(如 Nginx)可前置以支持 TLS 终止与负载均衡。
|
||||
|
||||
**章节来源**
|
||||
- [package.json:9-10](file://package.json#L9-L10)
|
||||
- [src/index.ts:55-65](file://src/index.ts#L55-L65)
|
||||
Reference in New Issue
Block a user