diff --git a/WebApp/src/服务端接口与WebSocket消息类型.md b/WebApp/src/服务端接口与WebSocket消息类型.md new file mode 100644 index 0000000..a25bc91 --- /dev/null +++ b/WebApp/src/服务端接口与WebSocket消息类型.md @@ -0,0 +1,561 @@ +# 服务端接口与 WebSocket 消息类型 + +## 一、HTTP REST API 接口 + +### 1.1 配置接口 + +**GET /config** +- **功能**: 获取服务器配置信息 +- **参数**: 无 +- **响应**: + ```json + { + "useWebSocket": boolean, + "startupMode": string, + "logging": string + } + ``` + +### 1.2 头像上传接口 + +**POST /api/upload/avatar** +- **功能**: 上传用户头像 +- **参数**: + - `userId` (body): 用户ID + - `avatar` (file): 头像文件 +- **响应**: + ```json + { + "success": boolean, + "avatarUrl": string, + "message": string + } + ``` + +### 1.3 会话管理接口 + +**GET /signaling/connection-ids** +- **功能**: 获取所有活跃的连接ID(无需会话认证) +- **响应**: + ```json + { + "connectionIds": string[], + "totalCount": number + } + ``` + +**PUT /signaling** +- **功能**: 创建新的会话,获取会话ID +- **参数**: 无 +- **响应**: + ```json + { + "sessionId": string + } + ``` + +**GET /signaling** +- **功能**: 获取当前会话的所有信令消息 +- **认证**: 需要在请求头 `Session-Id` 中提供会话ID +- **参数**: + - `fromtime` (query): 起始时间戳,用于增量拉取 +- **响应**: + ```json + { + "messages": [ + { + "connectionId": string, + "type": "connect|disconnect|offer|answer|candidate", + "datetime": number, + "sdp": string, + "polite": boolean, + "candidate": string, + "sdpMLineIndex": number, + "sdpMid": string + } + ], + "datetime": number + } + ``` + +**DELETE /signaling** +- **功能**: 删除当前会话及其所有连接 +- **认证**: 需要会话ID +- **响应**: 200 OK + +### 1.4 连接管理接口 + +**GET /signaling/connection** +- **功能**: 获取当前会话的连接列表 +- **认证**: 需要会话ID +- **响应**: + ```json + { + "connections": [ + { + "connectionId": string, + "type": "connect", + "datetime": number + } + ] + } + ``` + +**PUT /signaling/connection** +- **功能**: 创建新的连接 +- **认证**: 需要会话ID +- **请求体**: + ```json + { + "connectionId": string + } + ``` +- **响应**: + ```json + { + "connectionId": string, + "polite": boolean, + "type": "connect", + "datetime": number + } + ``` + +**DELETE /signaling/connection** +- **功能**: 删除指定的连接 +- **认证**: 需要会话ID +- **请求体**: + ```json + { + "connectionId": string + } + ``` +- **响应**: + ```json + { + "connectionId": string + } + ``` + +### 1.5 WebRTC 信令交换接口 + +**GET /signaling/offer** +- **功能**: 获取 offer 信令消息列表 +- **认证**: 需要会话ID +- **参数**: `fromtime` (query): 起始时间戳 +- **响应**: + ```json + { + "offers": [ + { + "connectionId": string, + "sdp": string, + "polite": boolean, + "type": "offer", + "datetime": number + } + ] + } + ``` + +**POST /signaling/offer** +- **功能**: 发送 offer 信令 +- **认证**: 需要会话ID +- **请求体**: + ```json + { + "connectionId": string, + "sdp": string + } + ``` +- **响应**: 200 OK + +**GET /signaling/answer** +- **功能**: 获取 answer 信令消息列表 +- **认证**: 需要会话ID +- **参数**: `fromtime` (query): 起始时间戳 +- **响应**: + ```json + { + "answers": [ + { + "connectionId": string, + "sdp": string, + "type": "answer", + "datetime": number + } + ] + } + ``` + +**POST /signaling/answer** +- **功能**: 发送 answer 信令 +- **认证**: 需要会话ID +- **请求体**: + ```json + { + "connectionId": string, + "sdp": string + } + ``` +- **响应**: 200 OK + +**GET /signaling/candidate** +- **功能**: 获取 ICE candidate 信令消息列表 +- **认证**: 需要会话ID +- **参数**: `fromtime` (query): 起始时间戳 +- **响应**: + ```json + { + "candidates": [ + { + "connectionId": string, + "candidate": string, + "sdpMLineIndex": number, + "sdpMid": string, + "type": "candidate", + "datetime": number + } + ] + } + ``` + +**POST /signaling/candidate** +- **功能**: 发送 ICE candidate 信令 +- **认证**: 需要会话ID +- **请求体**: + ```json + { + "connectionId": string, + "candidate": string, + "sdpMLineIndex": number, + "sdpMid": string + } + ``` +- **响应**: 200 OK + +### 1.6 房间信息接口 + +**GET /signaling/rooms** +- **功能**: 获取房间和用户信息 +- **认证**: 需要会话ID +- **响应**: + ```json + { + "rooms": [ + { + "roomId": string, + "users": [ + { + "sessionId": string, + "connected": boolean + } + ], + "userCount": number + } + ], + "totalRooms": number + } + ``` + +--- + +## 二、WebSocket 消息类型 + +### 2.1 连接生命周期消息 + +#### connect(连接建立) + +- **方向**: 客户端 → 服务端 → 客户端 +- **客户端发送**: + ```json + { + "type": "connect", + "connectionId": string + } + ``` +- **服务端响应**: + ```json + { + "type": "connect", + "connectionId": string, + "polite": boolean, + "role": "host|participant", + "participantId": string + } + ``` +- **说明**: 建立连接并协商 polite 标志以处理连接冲突。`polite=true` 表示后加入方(participant),`polite=false` 表示先加入方(host)。 + +#### disconnect(连接断开) + +- **方向**: 客户端 → 服务端 → 客户端 +- **客户端发送**: + ```json + { + "type": "disconnect", + "connectionId": string + } + ``` +- **服务端响应**: + ```json + { + "type": "disconnect", + "connectionId": string, + "reason": "normal|host-left" + } + ``` +- **说明**: 断开连接。当 host 离开时,reason 为 `host-left`。 + +#### participant-joined(参与者加入,仅私有模式) + +- **方向**: 服务端 → host 客户端 +- **格式**: + ```json + { + "type": "participant-joined", + "connectionId": string, + "participantId": string + } + ``` +- **说明**: 通知 host 有新的 participant 加入。 + +#### participant-left(参与者离开,仅私有模式) + +- **方向**: 服务端 → host 客户端 / 其他 participants +- **格式**: + ```json + { + "type": "participant-left", + "connectionId": string, + "participantId": string + } + ``` +- **说明**: 通知有 participant 离开房间。 + +### 2.2 WebRTC SDP 交换消息 + +#### offer + +- **方向**: 双向 +- **格式**: + ```json + { + "type": "offer", + "from": string, + "to": string, + "data": { + "sdp": string, + "connectionId": string, + "participantId": string + }, + "participantId": string + } + ``` +- **路由规则**: + - **私有模式**: Host → 所有/特定 Participant;Participant → Host + - **公共模式**: Peer → 所有其他 Peers + +#### answer + +- **方向**: 双向 +- **格式**: + ```json + { + "type": "answer", + "from": string, + "to": string, + "data": { + "sdp": string, + "connectionId": string, + "participantId": string + }, + "participantId": string + } + ``` +- **路由规则**: + - **私有模式**: Participant → Host;Host → 特定 Participant + - **公共模式**: Peer → 特定 Peer + +#### candidate + +- **方向**: 双向 +- **格式**: + ```json + { + "type": "candidate", + "from": string, + "to": string, + "data": { + "candidate": string, + "sdpMLineIndex": number, + "sdpMid": string, + "connectionId": string, + "participantId": string + }, + "participantId": string + } + ``` +- **路由规则**: 与 answer 消息相同。 + +### 2.3 心跳/控制消息 + +#### ping(服务端 → 客户端) + +- **格式**: + ```json + { + "from": string, + "to": string, + "type": "on-message", + "data": { + "type": "ping" + } + } + ``` +- **说明**: 服务端心跳检测(可选功能,默认未启用)。 + +#### pong(客户端 → 服务端) + +- **格式**: + ```json + { + "type": "pong" + } + ``` +- **说明**: 心跳应答。 + +### 2.4 自定义消息 + +#### on-message(通用消息传递) + +- **方向**: 双向 +- **格式**: + ```json + { + "type": "on-message", + "from": string, + "to": string, + "data": { + "message": string|object, + "connectionId": string, + "senderId": string, + "participantId": string + } + } + ``` +- **路由规则**: + - **私有模式**: Host ↔ 所有 Participants;Participant ↔ Host + - **公共模式**: Peer → Peer +- **说明**: 传输文本、数据或聊天消息。 + +#### broadcast(广播消息) + +- **客户端发送**: + ```json + { + "type": "broadcast", + "message": string|object, + "targetConnectionId": string + } + ``` +- **服务端转发**: + ```json + { + "type": "broadcast", + "message": string|object, + "from": "server" + } + ``` +- **说明**: 若指定 `targetConnectionId`,则广播给该连接组内的所有成员;否则广播给所有连接的客户端。 + +### 2.5 呼叫请求消息 + +#### call-request + +- **方向**: 客户端 → 服务端 → 客户端 +- **格式**: + ```json + { + "type": "call-request", + "data": string + } + ``` +- **路由规则**: + - **私有模式**: Participant → Server → Host + - **公共模式**: Peer → Server → 所有其他 Peers +- **说明**: 发起呼叫请求。 + +--- + +## 三、通信模式说明 + +### 3.1 公共模式(Public Mode) + +- 所有连接的客户端都可以相互通信 +- offer/answer/candidate 向所有其他客户端广播 + +### 3.2 私有模式(Private Mode) + +- 一个 connectionId 对应一个房间,包含 1 个 host 和多个 participants +- Host 是第一个加入 connectionId 的客户端(`polite=false`) +- Participants 是后续加入的客户端(`polite=true`) +- Host 发送的消息可单播给特定 participant 或广播给所有 participants +- Participant 发送的消息仅发送给 host +- 当 host 离开时,整个房间关闭,所有 participants 被断开连接 +- 当 participant 离开时,房间继续存在 + +--- + +## 四、会话与连接管理 + +### 4.1 会话(Session) + +- 每个客户端通过 `PUT /signaling` 创建一个唯一的会话 +- 会话ID通过 `Session-Id` 请求头在所有后续 HTTP 请求中传递 +- 会话管理超时:10 秒无请求则自动删除会话 +- 一个会话可以包含多个连接ID + +### 4.2 连接(Connection) + +- 连接ID由客户端生成和指定 +- 在 HTTP 模式下,连接对通过 connectionId 自动配对 +- 在 WebSocket 私有模式下,第一个连接是 host,后续连接是 participants +- 一个连接对在私有模式下支持 1 对多(1 host + N participants) + +### 4.3 Polite 标志 + +- 用于处理 WebRTC 连接冲突(双方同时发送 offer 的情况) +- `polite=true`:该端应放弃 offer,接收来自另一端的 offer +- `polite=false`:该端具有优先权,可以发送 offer +- 私有模式下:host → `polite=false`,participants → `polite=true` + +--- + +## 五、关键设计特性 + +1. **双协议支持**: 同时支持 HTTP 轮询和 WebSocket 两种信令协议 +2. **两种通信模式**: 公共模式(全连通)和私有模式(1 对多房间) +3. **Polite 机制**: 自动处理并发 offer 冲突 +4. **会话隔离**: 通过 Session-ID 在 HTTP 模式下隔离不同客户端的消息 +5. **心跳检测**: 可选的心跳机制防止连接超时 +6. **消息增量拉取**: HTTP GET 支持 fromtime 参数实现增量消息获取 +7. **头像上传**: 内置文件上传功能支持用户头像管理 +8. **API 文档**: 集成 Swagger 提供自动生成的 API 文档 + +--- + +## 六、源文件路径 + +| 文件 | 功能 | +|------|------| +| `src/index.ts` | 应用入口,配置和启动服务器 | +| `src/server.ts` | Express 服务器创建,中间件配置 | +| `src/signaling.ts` | HTTP REST 路由定义 | +| `src/websocket.ts` | WebSocket 服务器和消息路由 | +| `src/class/httphandler.ts` | HTTP 处理器,核心业务逻辑 | +| `src/class/websockethandler.ts` | WebSocket 处理器,消息分发逻辑 | +| `src/class/offer.ts` | Offer 类定义 | +| `src/class/answer.ts` | Answer 类定义 | +| `src/class/candidate.ts` | Candidate 类定义 | +| `src/class/options.ts` | 配置选项接口 | +| `src/swagger.ts` | Swagger API 文档配置 | +| `src/log.ts` | 日志工具 |