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

21 KiB
Raw Permalink Blame History

双向通信示例

**本文档引用的文件** - [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)

目录

  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、日志与多种运行参数。
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

图表来源

章节来源

核心组件

  • 双向页面控制器:负责 UI 控件初始化、媒体设备选择、分辨率与编解码器配置、启动本地媒体、建立 WebRTC 连接、处理统计信息与错误。
  • 媒体处理类:封装本地媒体流采集、本地/远端视频播放、本地轨道获取与远端轨道添加。
  • 信令封装:提供 HTTP 与 WebSocket 两种信令实现统一事件接口负责会话创建、offer/answer/candidate 发送与接收。
  • 连接管理:封装多个 Peer 的生命周期与事件路由,负责 SDP 协商、ICE 候选处理、统计数据查询与消息广播。
  • Peer 封装:封装 RTCPeerConnection 生命周期、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。
  • 配置与 ICE提供 RTC 配置sdpSemantics、ICE 服务器、音频增强)、读取/写入本地存储的 STUN/TURN 服务器。

章节来源

架构总览

下图展示了从用户操作到媒体流双向传输的整体架构与数据流。

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

  • 功能职责
    • 设备与分辨率选择:枚举媒体设备,填充视频/音频选择框;支持预设分辨率与自定义分辨率。
    • 本地媒体启动:根据选择的设备与分辨率调用媒体采集,设置本地视频元素并自动播放。
    • 连接建立:根据配置选择 HTTP 或 WebSocket 信令,创建 RenderStreaming 实例,注册连接事件回调,添加本地轨道,设置编解码器偏好,启动统计信息展示。
    • 断开连接:清理统计信息、停止渲染、删除连接、恢复 UI 状态。
    • 编解码器偏好:在支持的浏览器上动态设置视频编解码器偏好,影响远端接收质量。
    • 统计信息:周期性获取 WebRTC 统计并格式化显示,包含本地/远端分辨率与关键指标。
  • 关键流程
    • 启动本地视频:禁用输入控件,计算分辨率,调用媒体采集,启用设置按钮。
    • 设置连接:创建信令实例,准备 RenderStreaming注册 onConnect/onDisconnect/onTrackEvent 回调,添加本地轨道,设置编解码器,启动统计。
    • 断开连接:清除统计,删除连接,停止渲染,恢复 UI。
  • 最佳实践
    • 使用统一的 RTC 配置与媒体约束,避免分辨率不匹配导致的性能问题。
    • 在支持的浏览器上启用编解码器偏好,提升兼容性与质量。
    • 注意统计信息的周期性更新与清理,防止内存泄漏。

章节来源

媒体处理SendVideosendvideo.js

  • 功能职责
    • 本地媒体采集:根据设备 ID 与分辨率约束获取媒体流,设置本地视频元素并播放。
    • 本地轨道获取:返回本地媒体流中的轨道集合,供连接管理添加到 PeerConnection。
    • 远端轨道添加:创建或复用 MediaStream将远端轨道加入驱动远端视频播放。
  • 关键流程
    • startLocalVideo构造约束调用 getUserMedia设置本地视频源并播放。
    • getLocalTracks从本地流获取轨道。
    • addRemoteTrack将远端轨道加入本地流。
  • 最佳实践
    • 在调用 getUserMedia 前检查权限与设备可用性。
    • 使用 MediaStreamTrack 的 addTrack/removeTrack 精确管理轨道生命周期。

章节来源

信令封装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 模式下注意连接状态与重连策略,确保消息可靠传递。

章节来源

连接管理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避免资源泄露。

章节来源

Peer 封装RTCPeerConnection 管理peer.js

  • 功能职责
    • 封装 RTCPeerConnection 的创建、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。
    • 提供防闪烁glare处理避免同时进行 offer/answer 导致的状态冲突。
  • 关键流程
    • _onNegotiationneededsetLocalDescription 生成 offer派发 sendoffer 事件。
    • onGotDescription根据描述类型设置远端描述必要时生成 answer 并派发 sendanswer。
    • onIceCandidate收集候选并通过事件派发 sendcandidate。
    • loopResendOffer定期重发未确认的 offer保证协商完成。
  • 最佳实践
    • 正确处理 signalingState 与 iceConnectionState及时响应失败事件。
    • 在多参与者场景下,确保 participantId 与 Peer 的一一对应。

章节来源

配置与 ICERTC 配置与 STUN/TURNconfig.js, icesettings.js

  • 功能职责
    • getServerConfig获取服务器配置是否使用 WebSocket、启动模式等
    • getRTCConfiguration设置 sdpSemantics、ICE 服务器、媒体约束与音频增强选项。
    • icesettings.js管理本地存储的 STUN/TURN 服务器列表,支持增删改查与默认值。
  • 最佳实践
    • 在公网部署时配置可靠的 TURN 服务器,提升连通性。
    • 根据网络环境调整 ICE 服务器列表与轮询间隔。

章节来源

后端WebSocket 信令服务器websocket.ts

  • 功能职责
    • 接收并处理 WebSocket 消息,根据类型分发到处理器,支持 connect/disconnect/offer/answer/candidate/ping/pong/broadcast/on-message 等。
    • 维护连接池与广播机制,支持心跳检测与消息路由。
  • 关键流程
    • connection 事件:添加新连接到处理器。
    • onmessage解析消息类型并调用相应处理器。
    • onclose移除关闭连接。
  • 最佳实践
    • 对未知消息类型进行安全处理,避免异常传播。
    • 实现心跳与超时检测,维持长连接稳定性。

章节来源

入口与参数服务器启动index.ts

  • 功能职责
    • 解析命令行参数支持端口、HTTPS、信令类型、通信模式与日志级别。
    • 根据参数启动 HTTP/HTTPS 服务器,初始化 WebSocket 信令服务。
  • 关键流程
    • 参数解析:读取 PORT/SECURE/KEYFILE/CERTFILE/TYPE/MODE/LOGGING。
    • 服务器启动:根据 secure 决定 HTTP/HTTPS监听端口并输出访问地址。
    • 信令类型:仅支持 websocket 或 http其他值将被修正为 websocket。
  • 最佳实践
    • 生产环境建议启用 HTTPS 并配置有效的证书文件。
    • 根据部署环境选择合适的通信模式public/private

章节来源

依赖关系分析

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 : "继承"

图表来源

性能考虑

  • 分辨率与编解码器
    • 合理选择分辨率,避免过高分辨率导致带宽与 CPU 压力过大。
    • 在支持的浏览器上设置编解码器偏好,提升解码效率与画质一致性。
  • ICE 服务器
    • 在公网部署时配置可靠的 STUN/TURN 服务器,减少打洞失败与延迟。
  • 统计信息
    • 定期但不过度地获取 WebRTC 统计,避免频繁 IO 影响性能。
  • 信令类型
    • WebSocket 信令相比 HTTP 轮询具有更低的延迟与更少的请求开销,推荐在生产环境使用。

故障排除指南

  • 浏览器不支持编解码器偏好
    • 现象:无法设置编解码器偏好,界面提示当前浏览器不支持。
    • 处理:忽略偏好设置,使用默认编解码器;或升级浏览器版本。
  • 公共模式限制
    • 现象:公共模式下页面显示警告,示例不可用。
    • 处理:切换到私有模式或使用支持的部署方式。
  • 本地媒体无法启动
    • 现象:点击“启动视频”无反应或报错。
    • 处理:检查摄像头/麦克风权限、设备 ID 是否正确、分辨率是否受支持。
  • 连接无法建立
    • 现象:点击“设置连接”后无进展或很快断开。
    • 处理检查信令服务器是否在线、WebSocket 是否连通、ICE 服务器配置是否正确。
  • 远端无画面
    • 现象:本地有画面但远端无画面。
    • 处理:确认已添加本地轨道到 PeerConnection、远端轨道已加入本地流、统计信息显示远端轨道已接收。
  • 服务器启动失败
    • 现象:启动脚本报错或无法访问。
    • 处理检查端口占用、HTTPS 证书文件是否存在、命令行参数是否正确。

章节来源

结论

本双向通信示例通过清晰的模块划分与事件驱动设计,实现了从本地媒体采集到远端播放的完整链路。前端以 main.js 为核心协调各模块,后端以 WebSocket 信令服务器提供可靠的消息转发。通过合理的配置与最佳实践,可在不同网络环境下获得稳定的双向音视频体验。

附录

使用步骤

  • 启动服务器
    • 构建并启动后端服务,支持 HTTPS 与多种运行参数。
    • 示例脚本提供了构建与启动命令,也可直接运行 TypeScript 源码进行开发调试。
  • 打开示例页面
    • 在浏览器中访问服务器提供的地址,进入双向示例页面。
    • 页面会自动枚举可用的摄像头与麦克风,并提供分辨率与编解码器选择。
  • 建立连接
    • 输入连接 ID可随机生成点击“启动视频”采集本地媒体。
    • 点击“设置连接”,系统将创建信令连接并开始协商。
    • 成功后本地与远端视频将分别显示,统计信息会周期性更新。
  • 进行双向通信
    • 在双方都完成连接后,即可实现视频与音频的双向传输。
    • 可通过挂断按钮结束连接,清理资源并恢复 UI 状态。

章节来源

常见配置选项

  • 服务器参数
    • 端口:-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可通过本地存储配置。
    • 音频增强:回声消除、噪声抑制、自动增益、高通滤波、打字噪声检测等。
  • 编解码器偏好
    • 在支持的浏览器上可选择特定视频编解码器,提升兼容性与质量。

章节来源

代码示例路径