Files
video_socket-server/.qoder/repowiki/zh/content/客户端示例/双向通信示例.md

444 lines
21 KiB
Markdown
Raw Permalink Normal View History

2026-05-16 13:24:02 +08:00
# 双向通信示例
<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)