# 信令客户端

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