Files
video_socket-server/.qoder/repowiki/zh/content/客户端示例/双向通信示例.md
2026-05-16 13:24:02 +08:00

444 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 双向通信示例
<cite>
**本文档引用的文件**
- [main.js](file://client/public/bidirectional/js/main.js)
- [sendvideo.js](file://client/public/bidirectional/js/sendvideo.js)
- [index.html](file://client/public/bidirectional/index.html)
- [style.css](file://client/public/bidirectional/css/style.css)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
- [signaling.js](file://client/src/signaling.js)
- [peer.js](file://client/src/peer.js)
- [renderstreaming.js](file://client/src/renderstreaming.js)
- [index.ts](file://src/index.ts)
- [websocket.ts](file://src/websocket.ts)
- [options.ts](file://src/class/options.ts)
- [package.json](file://package.json)
- [run.bat](file://run.bat)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本示例演示了基于 WebRTC 的双向音视频通信涵盖从本地媒体采集、信令交互、PeerConnection 建立到媒体流收发的完整流程。前端通过双向页面控制本地摄像头与麦克风,选择分辨率与编解码器偏好,发起连接后自动建立点对点通道,实现本地预览与远端播放的双向传输。后端提供 WebSocket 信令服务器,负责转发 offer/answer/candidate 等信令消息,并支持公共与私有通信模式。
## 项目结构
该项目采用前后端分离架构:
- 前端部分位于 client/public 与 client/src包含双向示例页面、通用配置与工具模块、WebRTC 连接管理与信令封装。
- 后端部分位于 src提供 Express 应用与 WebSocket 信令服务,支持 HTTPS、日志与多种运行参数。
```mermaid
graph TB
subgraph "前端"
A["bidirectional 页面<br/>index.html"]
B["主控制器<br/>main.js"]
C["媒体处理<br/>sendvideo.js"]
D["信令封装<br/>signaling.js"]
E["连接管理<br/>renderstreaming.js"]
F["Peer 封装<br/>peer.js"]
G["配置与ICE<br/>config.js / icesettings.js"]
end
subgraph "后端"
H["入口与参数解析<br/>index.ts"]
I["WebSocket 信令<br/>websocket.ts"]
J["选项接口<br/>options.ts"]
end
A --> B
B --> C
B --> D
B --> E
E --> F
D --> I
H --> I
G --> B
```
图表来源
- [index.html:1-84](file://client/public/bidirectional/index.html#L1-L84)
- [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383)
- [sendvideo.js:1-54](file://client/public/bidirectional/js/sendvideo.js#L1-L54)
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
- [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317)
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
- [config.js:1-39](file://client/public/js/config.js#L1-L39)
- [icesettings.js:1-104](file://client/public/js/icesettings.js#L1-L104)
- [index.ts:1-109](file://src/index.ts#L1-L109)
- [websocket.ts:1-118](file://src/websocket.ts#L1-L118)
章节来源
- [index.html:1-84](file://client/public/bidirectional/index.html#L1-L84)
- [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383)
- [index.ts:1-109](file://src/index.ts#L1-L109)
## 核心组件
- 双向页面控制器:负责 UI 控件初始化、媒体设备选择、分辨率与编解码器配置、启动本地媒体、建立 WebRTC 连接、处理统计信息与错误。
- 媒体处理类:封装本地媒体流采集、本地/远端视频播放、本地轨道获取与远端轨道添加。
- 信令封装:提供 HTTP 与 WebSocket 两种信令实现统一事件接口负责会话创建、offer/answer/candidate 发送与接收。
- 连接管理:封装多个 Peer 的生命周期与事件路由,负责 SDP 协商、ICE 候选处理、统计数据查询与消息广播。
- Peer 封装:封装 RTCPeerConnection 生命周期、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。
- 配置与 ICE提供 RTC 配置sdpSemantics、ICE 服务器、音频增强)、读取/写入本地存储的 STUN/TURN 服务器。
章节来源
- [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383)
- [sendvideo.js:1-54](file://client/public/bidirectional/js/sendvideo.js#L1-L54)
- [signaling.js:1-292](file://client/src/signaling.js#L1-L292)
- [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317)
- [peer.js:1-188](file://client/src/peer.js#L1-L188)
- [config.js:1-39](file://client/public/js/config.js#L1-L39)
- [icesettings.js:1-104](file://client/public/js/icesettings.js#L1-L104)
## 架构总览
下图展示了从用户操作到媒体流双向传输的整体架构与数据流。
```mermaid
sequenceDiagram
participant U as "用户"
participant UI as "双向页面(main.js)"
participant SV as "媒体处理(sendvideo.js)"
participant RS as "连接管理(renderstreaming.js)"
participant P as "Peer 封装(peer.js)"
participant SIG as "信令封装(signaling.js)"
participant WS as "WebSocket 信令(backend)"
U->>UI : "启动视频/设置连接"
UI->>SV : "startLocalVideo()"
SV-->>UI : "本地媒体流可用"
UI->>SIG : "创建信令实例(HTTP/WebSocket)"
UI->>RS : "start()/createConnection()"
RS->>P : "_preparePeerConnection()"
P-->>RS : "onNegotiationneeded -> 发送 offer"
RS->>SIG : "sendOffer()"
SIG->>WS : "转发 offer"
WS-->>SIG : "广播/定向转发"
SIG-->>RS : "on offer"
RS->>P : "onGotDescription(offer)"
P-->>RS : "setLocalDescription -> answer"
RS->>SIG : "sendAnswer()"
SIG->>WS : "转发 answer"
WS-->>SIG : "广播/定向转发"
SIG-->>RS : "on answer"
RS->>P : "onGotDescription(answer)"
P-->>RS : "稳定状态"
RS-->>UI : "onTrackEvent -> 添加远端轨道"
UI->>SV : "addRemoteTrack()"
SV-->>UI : "远端视频播放"
```
图表来源
- [main.js:146-184](file://client/public/bidirectional/js/main.js#L146-L184)
- [sendvideo.js:44-52](file://client/public/bidirectional/js/sendvideo.js#L44-L52)
- [renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250)
- [peer.js:57-82](file://client/src/peer.js#L57-L82)
- [signaling.js:152-292](file://client/src/signaling.js#L152-L292)
- [websocket.ts:44-115](file://src/websocket.ts#L44-L115)
## 详细组件分析
### 主控制器双向页面逻辑main.js
- 功能职责
- 设备与分辨率选择:枚举媒体设备,填充视频/音频选择框;支持预设分辨率与自定义分辨率。
- 本地媒体启动:根据选择的设备与分辨率调用媒体采集,设置本地视频元素并自动播放。
- 连接建立:根据配置选择 HTTP 或 WebSocket 信令,创建 RenderStreaming 实例,注册连接事件回调,添加本地轨道,设置编解码器偏好,启动统计信息展示。
- 断开连接:清理统计信息、停止渲染、删除连接、恢复 UI 状态。
- 编解码器偏好:在支持的浏览器上动态设置视频编解码器偏好,影响远端接收质量。
- 统计信息:周期性获取 WebRTC 统计并格式化显示,包含本地/远端分辨率与关键指标。
- 关键流程
- 启动本地视频:禁用输入控件,计算分辨率,调用媒体采集,启用设置按钮。
- 设置连接:创建信令实例,准备 RenderStreaming注册 onConnect/onDisconnect/onTrackEvent 回调,添加本地轨道,设置编解码器,启动统计。
- 断开连接:清除统计,删除连接,停止渲染,恢复 UI。
- 最佳实践
- 使用统一的 RTC 配置与媒体约束,避免分辨率不匹配导致的性能问题。
- 在支持的浏览器上启用编解码器偏好,提升兼容性与质量。
- 注意统计信息的周期性更新与清理,防止内存泄漏。
章节来源
- [main.js:112-184](file://client/public/bidirectional/js/main.js#L112-L184)
- [main.js:220-241](file://client/public/bidirectional/js/main.js#L220-L241)
- [main.js:289-303](file://client/public/bidirectional/js/main.js#L289-L303)
- [main.js:337-367](file://client/public/bidirectional/js/main.js#L337-L367)
### 媒体处理SendVideosendvideo.js
- 功能职责
- 本地媒体采集:根据设备 ID 与分辨率约束获取媒体流,设置本地视频元素并播放。
- 本地轨道获取:返回本地媒体流中的轨道集合,供连接管理添加到 PeerConnection。
- 远端轨道添加:创建或复用 MediaStream将远端轨道加入驱动远端视频播放。
- 关键流程
- startLocalVideo构造约束调用 getUserMedia设置本地视频源并播放。
- getLocalTracks从本地流获取轨道。
- addRemoteTrack将远端轨道加入本地流。
- 最佳实践
- 在调用 getUserMedia 前检查权限与设备可用性。
- 使用 MediaStreamTrack 的 addTrack/removeTrack 精确管理轨道生命周期。
章节来源
- [sendvideo.js:15-35](file://client/public/bidirectional/js/sendvideo.js#L15-L35)
- [sendvideo.js:40-52](file://client/public/bidirectional/js/sendvideo.js#L40-L52)
### 信令封装HTTP 与 WebSocketsignaling.js
- 功能职责
- HTTP 信令:基于轮询的信令实现,维护 Session-Id支持连接创建/删除、offer/answer/candidate 发送与接收,以及自定义消息。
- WebSocket 信令:基于 WebSocket 的实时信令实现,支持连接/断开、offer/answer/candidate、参与者加入/离开、广播消息等。
- 关键流程
- HTTPstart() 循环创建会话loopGetAll() 轮询消息并分发事件createConnection/deleteConnection/sendOffer/sendAnswer/sendCandidate。
- WebSocketonopen/onmessage/onclose 处理消息类型映射分发事件createConnection/deleteConnection/sendOffer/sendAnswer/sendCandidate/sendMessage。
- 最佳实践
- 在 HTTP 模式下合理设置轮询间隔,避免频繁请求。
- WebSocket 模式下注意连接状态与重连策略,确保消息可靠传递。
章节来源
- [signaling.js:30-97](file://client/src/signaling.js#L30-L97)
- [signaling.js:152-292](file://client/src/signaling.js#L152-L292)
### 连接管理RenderStreamingrenderstreaming.js
- 功能职责
- 统一封装信令事件与 Peer 生命周期,支持单 Peerparticipant与多 Peerhost场景。
- 路由 offer/answer/candidate 到对应 Peer处理 onTrack 事件,提供统计数据查询与消息广播。
- 关键流程
- _onConnect根据角色创建 Peer触发 onConnect。
- _onOffer/_onAnswer/_onIceCandidate根据是否为 host 与 participantId 路由到对应 Peer。
- _preparePeerConnection创建 Peer 并绑定事件,桥接信令与 Peer。
- getStats/addTrack/addTransceiver/createDataChannel按角色与 participantId 路由到具体 Peer。
- 最佳实践
- host 端应为每个参与者维护独立 Peer确保消息隔离。
- 在断开或失败状态下及时清理 Peer避免资源泄露。
章节来源
- [renderstreaming.js:40-70](file://client/src/renderstreaming.js#L40-L70)
- [renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250)
- [renderstreaming.js:252-290](file://client/src/renderstreaming.js#L252-L290)
### Peer 封装RTCPeerConnection 管理peer.js
- 功能职责
- 封装 RTCPeerConnection 的创建、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。
- 提供防闪烁glare处理避免同时进行 offer/answer 导致的状态冲突。
- 关键流程
- _onNegotiationneededsetLocalDescription 生成 offer派发 sendoffer 事件。
- onGotDescription根据描述类型设置远端描述必要时生成 answer 并派发 sendanswer。
- onIceCandidate收集候选并通过事件派发 sendcandidate。
- loopResendOffer定期重发未确认的 offer保证协商完成。
- 最佳实践
- 正确处理 signalingState 与 iceConnectionState及时响应失败事件。
- 在多参与者场景下,确保 participantId 与 Peer 的一一对应。
章节来源
- [peer.js:57-82](file://client/src/peer.js#L57-L82)
- [peer.js:132-173](file://client/src/peer.js#L132-L173)
- [peer.js:175-186](file://client/src/peer.js#L175-L186)
### 配置与 ICERTC 配置与 STUN/TURNconfig.js, icesettings.js
- 功能职责
- getServerConfig获取服务器配置是否使用 WebSocket、启动模式等
- getRTCConfiguration设置 sdpSemantics、ICE 服务器、媒体约束与音频增强选项。
- icesettings.js管理本地存储的 STUN/TURN 服务器列表,支持增删改查与默认值。
- 最佳实践
- 在公网部署时配置可靠的 TURN 服务器,提升连通性。
- 根据网络环境调整 ICE 服务器列表与轮询间隔。
章节来源
- [config.js:3-7](file://client/public/js/config.js#L3-L7)
- [config.js:9-38](file://client/public/js/config.js#L9-L38)
- [icesettings.js:94-104](file://client/public/js/icesettings.js#L94-L104)
### 后端WebSocket 信令服务器websocket.ts
- 功能职责
- 接收并处理 WebSocket 消息,根据类型分发到处理器,支持 connect/disconnect/offer/answer/candidate/ping/pong/broadcast/on-message 等。
- 维护连接池与广播机制,支持心跳检测与消息路由。
- 关键流程
- connection 事件:添加新连接到处理器。
- onmessage解析消息类型并调用相应处理器。
- onclose移除关闭连接。
- 最佳实践
- 对未知消息类型进行安全处理,避免异常传播。
- 实现心跳与超时检测,维持长连接稳定性。
章节来源
- [websocket.ts:27-39](file://src/websocket.ts#L27-L39)
- [websocket.ts:44-115](file://src/websocket.ts#L44-L115)
### 入口与参数服务器启动index.ts
- 功能职责
- 解析命令行参数支持端口、HTTPS、信令类型、通信模式与日志级别。
- 根据参数启动 HTTP/HTTPS 服务器,初始化 WebSocket 信令服务。
- 关键流程
- 参数解析:读取 PORT/SECURE/KEYFILE/CERTFILE/TYPE/MODE/LOGGING。
- 服务器启动:根据 secure 决定 HTTP/HTTPS监听端口并输出访问地址。
- 信令类型:仅支持 websocket 或 http其他值将被修正为 websocket。
- 最佳实践
- 生产环境建议启用 HTTPS 并配置有效的证书文件。
- 根据部署环境选择合适的通信模式public/private
章节来源
- [index.ts:14-44](file://src/index.ts#L14-L44)
- [index.ts:52-91](file://src/index.ts#L52-L91)
## 依赖关系分析
```mermaid
classDiagram
class SendVideo {
+startLocalVideo(videoSource, audioSource, width, height)
+getLocalTracks()
+addRemoteTrack(track)
}
class Signaling {
+start()
+stop()
+createConnection(connectionId)
+deleteConnection(connectionId)
+sendOffer(connectionId, sdp)
+sendAnswer(connectionId, sdp)
+sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex)
+sendMessage(connectionId, message)
}
class WebSocketSignaling {
+start()
+stop()
+createConnection(connectionId)
+deleteConnection(connectionId)
+sendOffer(connectionId, sdp, participantId)
+sendAnswer(connectionId, sdp, participantId)
+sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex, participantId)
+sendMessage(connectionId, message)
}
class Peer {
+ontrack
+ondatachannel
+onicecandidate
+onnegotiationneeded
+onsignalingstatechange
+oniceconnectionstatechange
+onicegatheringstatechange
+onGotDescription(connectionId, description)
+onGotCandidate(connectionId, candidate)
+getTransceivers(connectionId)
+addTrack(connectionId, track)
+addTransceiver(connectionId, trackOrKind, init)
+createDataChannel(connectionId, label)
+getStats(connectionId)
+close()
}
class RenderStreaming {
+start()
+stop()
+createConnection(connectionId)
+deleteConnection()
+addTrack(track, participantId)
+addTransceiver(trackOrKind, init, participantId)
+createDataChannel(label, participantId)
+getStats(participantId)
+sendMessage(message)
+getTransceivers(participantId)
}
SendVideo --> RenderStreaming : "提供本地轨道"
RenderStreaming --> Signaling : "使用"
RenderStreaming --> Peer : "封装"
Signaling <|-- WebSocketSignaling : "继承"
```
图表来源
- [sendvideo.js:3-53](file://client/public/bidirectional/js/sendvideo.js#L3-L53)
- [signaling.js:3-150](file://client/src/signaling.js#L3-L150)
- [signaling.js:152-292](file://client/src/signaling.js#L152-L292)
- [peer.js:3-188](file://client/src/peer.js#L3-L188)
- [renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
## 性能考虑
- 分辨率与编解码器
- 合理选择分辨率,避免过高分辨率导致带宽与 CPU 压力过大。
- 在支持的浏览器上设置编解码器偏好,提升解码效率与画质一致性。
- ICE 服务器
- 在公网部署时配置可靠的 STUN/TURN 服务器,减少打洞失败与延迟。
- 统计信息
- 定期但不过度地获取 WebRTC 统计,避免频繁 IO 影响性能。
- 信令类型
- WebSocket 信令相比 HTTP 轮询具有更低的延迟与更少的请求开销,推荐在生产环境使用。
## 故障排除指南
- 浏览器不支持编解码器偏好
- 现象:无法设置编解码器偏好,界面提示当前浏览器不支持。
- 处理:忽略偏好设置,使用默认编解码器;或升级浏览器版本。
- 公共模式限制
- 现象:公共模式下页面显示警告,示例不可用。
- 处理:切换到私有模式或使用支持的部署方式。
- 本地媒体无法启动
- 现象:点击“启动视频”无反应或报错。
- 处理:检查摄像头/麦克风权限、设备 ID 是否正确、分辨率是否受支持。
- 连接无法建立
- 现象:点击“设置连接”后无进展或很快断开。
- 处理检查信令服务器是否在线、WebSocket 是否连通、ICE 服务器配置是否正确。
- 远端无画面
- 现象:本地有画面但远端无画面。
- 处理:确认已添加本地轨道到 PeerConnection、远端轨道已加入本地流、统计信息显示远端轨道已接收。
- 服务器启动失败
- 现象:启动脚本报错或无法访问。
- 处理检查端口占用、HTTPS 证书文件是否存在、命令行参数是否正确。
章节来源
- [main.js:99-105](file://client/public/bidirectional/js/main.js#L99-L105)
- [main.js:309-328](file://client/public/bidirectional/js/main.js#L309-L328)
- [index.ts:78-82](file://src/index.ts#L78-L82)
- [run.bat:8-16](file://run.bat#L8-L16)
## 结论
本双向通信示例通过清晰的模块划分与事件驱动设计,实现了从本地媒体采集到远端播放的完整链路。前端以 main.js 为核心协调各模块,后端以 WebSocket 信令服务器提供可靠的消息转发。通过合理的配置与最佳实践,可在不同网络环境下获得稳定的双向音视频体验。
## 附录
### 使用步骤
- 启动服务器
- 构建并启动后端服务,支持 HTTPS 与多种运行参数。
- 示例脚本提供了构建与启动命令,也可直接运行 TypeScript 源码进行开发调试。
- 打开示例页面
- 在浏览器中访问服务器提供的地址,进入双向示例页面。
- 页面会自动枚举可用的摄像头与麦克风,并提供分辨率与编解码器选择。
- 建立连接
- 输入连接 ID可随机生成点击“启动视频”采集本地媒体。
- 点击“设置连接”,系统将创建信令连接并开始协商。
- 成功后本地与远端视频将分别显示,统计信息会周期性更新。
- 进行双向通信
- 在双方都完成连接后,即可实现视频与音频的双向传输。
- 可通过挂断按钮结束连接,清理资源并恢复 UI 状态。
章节来源
- [package.json:9-12](file://package.json#L9-L12)
- [run.bat:8-16](file://run.bat#L8-L16)
- [index.html:76-79](file://client/public/bidirectional/index.html#L76-L79)
- [main.js:112-184](file://client/public/bidirectional/js/main.js#L112-L184)
### 常见配置选项
- 服务器参数
- 端口:-p/--port默认 80 或环境变量 PORT。
- HTTPS-s/--secure默认启用需提供 keyfile 与 certfile。
- 信令类型:-t/--typewebsocket 或 http默认 websocket。
- 通信模式:-m/--modepublic 或 private默认 public。
- 日志级别:-l/--loggingcombined/dev/short/tiny/none默认 dev。
- RTC 配置
- sdpSemantics统一计划unified-plan
- ICE 服务器STUN/TURN可通过本地存储配置。
- 音频增强:回声消除、噪声抑制、自动增益、高通滤波、打字噪声检测等。
- 编解码器偏好
- 在支持的浏览器上可选择特定视频编解码器,提升兼容性与质量。
章节来源
- [index.ts:20-41](file://src/index.ts#L20-L41)
- [config.js:9-38](file://client/public/js/config.js#L9-L38)
- [icesettings.js:94-104](file://client/public/js/icesettings.js#L94-L104)
- [main.js:189-213](file://client/public/bidirectional/js/main.js#L189-L213)
### 代码示例路径
- 双向页面主逻辑:[main.js:146-184](file://client/public/bidirectional/js/main.js#L146-L184)
- 媒体处理类:[sendvideo.js:15-35](file://client/public/bidirectional/js/sendvideo.js#L15-L35)
- 信令封装HTTP/WebSocket[signaling.js:152-292](file://client/src/signaling.js#L152-L292)
- 连接管理:[renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250)
- Peer 封装:[peer.js:57-82](file://client/src/peer.js#L57-L82)
- 服务器启动与参数:[index.ts:52-91](file://src/index.ts#L52-L91)