webrtc RTC_P2P源码解析

WebRTC 的 rtc_p2p 模块(位于 src/p2p/ 目录下)是实现 ICE (Interactive Connectivity Establishment) 协议的核心部分。它的主要任务是在两个对等端(Peer)之间寻找最佳的网络路径,以便建立直接的 UDP 连接进行媒体传输。

如果直连失败,它还负责通过 TURN 服务器中继数据。

以下是 rtc_p2p 源码的核心架构、关键类和流程解析:

一、 核心架构子系统

  1. 基础网络抽象 (base/):

• 封装了 Socket、地址、STUN/TURN 协议解析。

• 核心类:StunServer, TurnServer, PortInterface, Candidate.

  1. 端口管理 (base/ & client/):

• 负责创建不同类型的网络"端口"(即本地网络接口或隧道)。

• 核心类:UDPPort, TCPPort, RelayPort (TURN), StunPort.

  1. ICE 传输通道 (base/):

• 管理候选者对的连接检查(Connectivity Checks)。

• 核心类:P2PTransportChannel, Connection, ConnectionRequest.

  1. 会话管理 (session/):

• 高层封装,将 ICE 逻辑与 SDP Offer/Answer 结合。

• 核心类:JsepTransportController, IceTransportInternal.

二、关键概念和类

2.1 Candidate (候选者)

在 ICE 中,"Candidate" 是一个可能的通信端点(IP:Port + 协议 + 类型)。

• Host Candidate: 本地网卡 IP。

• Srflx (Server Reflexive): 通过 STUN 服务器获取的公网映射 IP。

• Relay Candidate: 通过 TURN 服务器分配的中继 IP。

• Prflx (Peer Reflexive): 在连接检查过程中发现的对方映射地址。

代码位置: p2p/base/candidate.h

cpp 复制代码
class Candidate {
 public:
  // 类型: HOST, SRFLX, RELAY, PRFLX
  enum Type { HOST = 0, SRFLX = 1, RELAY = 2, PRFLX = 3 };
  
  const rtc::SocketAddress& address() const;
  const std::string& type() const;
  int priority() const; // ICE 优先级,用于排序
};

2.2 Port (端口)

Port 是 WebRTC 中对网络接口的抽象。每个 Port 负责生成一组 Candidate,并发送/接收 STUN 包。

• UDPPort: 绑定本地 UDP 端口,生成 Host Candidate。

• StunPort: 向 STUN 服务器发送 Binding Request,生成 Srflx Candidate。

• RelayPort: 向 TURN 服务器发送 Allocate Request,生成 Relay Candidate,并处理后续的数据中继。

代码位置: p2p/base/port.h, p2p/base/udp_port.h, p2p/base/stun_port.h, p2p/base/relay_port.h

2.3 P2PTransportChannel (传输通道)

这是 ICE 算法的大脑。它持有所有的本地 Ports 和远程 Candidates。

• 职责:

  1. 配对 (Pairing): 将本地 Candidate 与远程 Candidate 组合成 Connection 对象。

  2. 排序 (Sorting): 根据 ICE 规则(优先级、网络类型)对 Connection 进行排序。

  3. 连接检查 (Connectivity Checks): 按照排序顺序,依次发送 STUN Binding Request 来测试连通性。

  4. 提名 (Nomination): 一旦某个 Connection 检查成功,将其标记为"选中",后续媒体数据将通过该路径发送。

代码位置: p2p/base/p2p_transport_channel.h

2.4 Connection (连接)

代表一个具体的"本地 Candidate <-> 远程 Candidate"的对子。

• 每个 Connection 维护自己的状态机(Init -> Connected -> Failed)。

• 负责发送具体的 STUN Ping 包并等待 Pong。

代码位置: p2p/base/connection.h

三、ICE 工作流程源码追踪

第一步:收集候选者 (Gathering)

当 PeerConnection 创建时,会触发 ICE 收集过程。

  1. 创建 Ports: P2PTransportChannel 调用 PortAllocator(通常在 pc/ 层)创建各种 Port。
cpp 复制代码
// 伪代码
auto udp_port = allocator->CreateUDPPort();
auto stun_port = allocator->CreateStunPort(stun_server);
auto relay_port = allocator->CreateRelayPort(turn_server);
  1. 生成 Candidate: 每个 Port 在初始化完成后,会通过信号(Signal)通知 Channel 它发现了新的 Candidate。
cpp 复制代码
// 在 UDPPort 中
SignalAddressReady(this, candidate); 
  1. 发送给对端: P2PTransportChannel 收到 Candidate 后,通过 JsepTransportController 将其放入 SDP 或作为 Trickle ICE 消息发送给对端。

第二步:远程候选者处理与配对

当收到对端的 SDP 或 Trickle ICE 消息时:

  1. 添加远程 Candidate: P2PTransportChannel::AddRemoteCandidate() 被调用。

  2. 创建 Connection: Channel 遍历所有本地 Candidate 和新收到的远程 Candidate,为每一对创建一个 Connection 对象。

cpp 复制代码
for (local_cand : local_candidates_) {
  for (remote_cand : remote_candidates_) {
    CreateConnection(local_cand, remote_cand);
  }
}
  1. 排序: 调用 SortConnectionsAndUpdateState()。ICE 规则规定:

• 优先检查高优先级的对子。

• 优先检查同类型的网络(如 Host-Host 优于 Relay-Relay)。

第三步:连接检查 (Connectivity Checks)

这是 ICE 的核心循环。

  1. 发送 Ping: P2PTransportChannel 按顺序取出未检查的 Connection,调用 conn->Ping()。 这会构造一个 STUN Binding Request,包含 ICE-CONTROLLED 或 ICE-CONTROLLING 属性,以及 Tie-breaker。

  2. 处理响应:

• 成功: 收到 STUN Binding Response。Connection 状态变为 STATE_WRITABLE。

• 失败: 超时或收到错误响应。Connection 状态变为 STATE_FAILED。

  1. 提名 (Nomination): 如果是主动方(Controlling),当第一个 Connection 变Writable时,会在后续的 STUN 包中设置 USE-CANDIDATE 属性。对端收到后,确认该连接为最终选择。

第四步:数据传输

一旦 Connection 被提名并确认为 Writable:

• P2PTransportChannel 将媒体数据(RTP/RTCP)交给该 Connection。

• Connection 通过其关联的 Port 的 Socket 发送数据。

• 如果是 Relay Connection,数据先发给 TURN 服务器,由 TURN 服务器转发给对端。

四、部分文件介绍

|----------------------------------------|----------------------------------------|
| 文件路径 | 作用 |
| p2p/base/candidate.h | 定义 ICE Candidate 数据结构 |
| p2p/base/port.h | 定义 Port 接口,所有端口类型的基类 |
| p2p/base/udp_port.h/cc | 实现本地 UDP 端口,生成 Host Candidate |
| p2p/base/stun_port.h/cc | 实现 STUN 客户端逻辑,生成 Srflx Candidate |
| p2p/base/relay_port.h/cc | 实现 TURN 客户端逻辑,生成 Relay Candidate |
| p2p/base/p2p_transport_channel.h/cc | 核心类:管理 ICE 状态机、配对、排序、Ping 调度 |
| p2p/base/connection.h/cc | 代表单个候选者对,执行具体的 STUN Ping/Pong |
| p2p/base/stun_request.h/cc | 封装 STUN 请求的发送、重传和超时处理 |
| p2p/base/basic_packet_socket_factory.h | 创建底层 UDP/TCP Socket 的工厂 |
| p2p/client/basic_port_allocator.cc | 协调创建各种 Port 的逻辑(通常被 PeerConnection 调用) |

五、常用调试和优化

  1. ICE 速度慢:

• 检查 P2PTransportChannel 中的 SortConnectionsAndUpdateState。

• 查看是否因为 DNS 解析 STUN/TURN 域名耗时过长(异步解析可能阻塞配对)。

• 检查 kInitialPingInterval 等常量,调整 Ping 的频率。

  1. 连接失败:

• 查看 Connection 的状态日志。是 TIMEOUT 还是 REFUSED?

• 检查 NAT 类型。如果是 Symmetric NAT,必须依赖 TURN (Relay)。

• 检查防火墙是否拦截了 UDP 高位端口。

  1. TURN 中继流量大:

• 检查为什么 Host/Srflx 连接失败。

• 在 P2PTransportChannel 中,可以看到最终选中的 Connection 类型。如果是 RELAY,说明直连失败。

六、总结

rtc_p2p 模块是一个高度异步、基于状态机的网络探测引擎。

• 输入: 本地网络接口、STUN/TURN 服务器配置、远程 Candidate 列表。

• 处理: 并行探测所有可能的路径,通过 STUN 协议验证连通性,并根据 ICE 算法选出最优路径。

• 输出: 一个可用的、经过验证的 UDP 通道(Socket),供上层 RTP 模块使用。

理解 P2PTransportChannel 如何调度 Connection 的 Ping 操作,以及 Port 如何封装不同的网络协议(UDP/TCP/TLS),是掌握 WebRTC 网络连接机制的关键。

相关推荐
GV191rLvq1 天前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
乐观的夕阳1 天前
ASP.NET 异步页的实现方式
java·数据库·asp.net
管家婆客服中心1 天前
Server 2016系统安装IIS和ASP.NET框架
asp.net·server 2016
威武的花瓣1 天前
细说ASP.NET的各种异步操作
后端·asp.net·php
任性的芝麻1 天前
ASP.NET MVC 中的异步方式
后端·asp.net·mvc
业余程序员plus1 天前
PCIe ACS机制分析
p2p·pcie·acs·iommu·redirect·egress control·violation
任性的自行车1 天前
ASP.NET MVC 4路线图
后端·asp.net·mvc
换个昵称都难19 天前
webrtc peerconnection_server 模块介绍
运维·服务器·webrtc
terry60019 天前
5G视频短信服务商选型全攻略:通道资源、架构能力与成本评估2026最新标准
大数据·人工智能·5g·json·asp.net·信息与通信·数据库架构
EasyGBS19 天前
延迟直降90%!国标GB28181视频平台EasyGBS支持WebRTC WHIP推流设备接入,让万物互联更简单
音视频·webrtc