宏观流程:
- 设置 local sdp;
- 创建一个 transport 对象(启用了 bundle);
- 收集 local candidates;
- 设置 remote sdp,添加 remote candidates;
- ICE 连通性检查,建立 P2P 连接;
- P2P 数据传输;
P2P 关键类:
- AllocationSequence: 负责对单个网络设备(Network 对象)分配 port,分阶段进行;
- 各种 port 类:代表的是一种通讯机制的本地实例,它可以和远端的类似实例一起实现数据通讯;
- Connection: 代表的是一个 local port 和一个 remote port 的通讯链接;
- PeerConnection: WebRTC 核心类;
- JsepTransportController: 管理 P2P 连接;
- 各种 transport 类:P2P 连接的封装,封装了加解密、mux/demux 等逻辑,提供收发数据的接口;
- BasicPortAllocator, PortAllocator: 保存各种配置,管理 PortAllocatorSession;
- BasicPortAllocatorSession, PortAllocatorSession: 遍历所有网络设备(Network 对象),分配 port;
各种 transport 类的关系 :
- PacketTransportInternal, PacketTransportInterface: packet base transport 的基类;
- P2PTransportChannel, IceTransportInternal: 继承自 PacketTransportInternal,负责 ICE 相关的功能,包括:收集本地 candidate、和远端 candidate 做连通性检查、数据传输;
- DtlsTransport, DtlsTransportInternal: 继承自 PacketTransportInternal,负责 DTLS 相关的功能,包括:DTLS 握手、DTLS 加解密;内含一个 IceTransportInternal.
(P2PTransportChannel),收发数据通过它实现;
不加密时,写入 DtlsTransport 的数据,直接交给 P2PTransportChannel;
加密时,数据会交给 SSLStreamAdapter -> StreamInterfaceChannel -> P2PTransportChannel,这是为了桥接加解密的流式接口与 DtlsTransport 的包式接口;
● DtlsTransport, DtlsTransportInternal: 继承自 PacketTransportInternal,负责 DTLS 相关的功能,包括:DTLS 握手、DTLS 加解密;内含一个 IceTransportInternal.
●DtlsSrtpTransport, SrtpTransport: DTLS-SRTP, SRTP 的实现类,继承自 RtpTransport;
●JsepTransport: JsepTransportController 管理 transport 的辅助类,sdp 里每个 m line 都对应于一个数据流(音频、视频、应用数据),每个数据流都需要一个 transport,但可以通过 bundle 技术复用同一个 transport,m line 里的 attribute 描述了 transport 的属性;
- 根据 transport 的加密属性,构造它时会准备无加密的 RtpTransport,或 SDES 加密的 SrtpTransport,或 DTLS 加密的 DtlsSrtpTransport;这一逻辑在
JsepTransportController::MaybeCreateJsepTransport
函数里;
- transport 对象将在设置 sdp 时创建,一个 transport 对象将会对应于一个最终的 P2P 网络连接(socket);
关键类的数量关系:
一个 PeerConnection - 一个 JsepTransportController - 一个 JsepTransport(启用了 bundle)。
一个 DtlsSrtpTransport - 一个 DtlsTransport - 一个 P2PTransportChannel。
一个 JsepTransportController - 一个 BasicPortAllocator - 多个 BasicPortAllocatorSession,但一次分配过程只会有一个 session。
一个 BasicPortAllocatorSession - 多个 AllocationSequence。
一个 AllocationSequence - 多个 port。
一个 P2PTransportChannel - 多个 Connection,但最终会选出一个 Connection 使用。
收集本地 candidate:
设置 local sdp,开始收集 candidate:
- PeerConnection::SetLocalDescription
- JsepTransportController::MaybeStartGathering
- P2PTransportChannel::MaybeStartGathering
- BasicPortAllocatorSession::StartGettingPorts
- BasicPortAllocatorSession::DoAllocate
DoAllocate:
DoAllocate 里会遍历所有网络设备(Network 对象),创建 AllocationSequence 对象,调用其 Init Start 函数,分配 port。
- BasicPortAllocatorSession::DoAllocate
- AllocationSequence::Start
- AllocationSequence::OnMessage
AllocationSequence 分配 port 分为三个 phase:UDP, RELAY, TCP。每个 phase 之间间隔一个 step delay。
UDP phase :
UDP phase 会收集两种类型的 candidate:host 和 srflx。
一旦创建了 AsyncPacketSocket 对象,有了本地 IP 和端口,host 类型的 candidate 也就已经就绪了,而 AsyncPacketSocket 对象在 AllocationSequence::Init
里就已经创建好了,所以可以直接发出 host candidate。
host candidate
- AllocationSequence::OnMessage
- AllocationSequence::CreateUDPPorts
- BasicPortAllocatorSession::AddAllocatedPort
- UDPPort::PrepareAddress
- UDPPort::OnLocalAddressReady
- Port::AddAddress
- sig slot (SignalCandidateReady)
- BasicPortAllocatorSession::OnCandidateReady
srflx candidate
RELAY phase
WebRTC 目前支持两种中继协议:GTURN 和 TURN。现在基本都是使用标准的 TURN 协议。TURN 协议是 STUN 协议的一个扩展,它利用一个中继服务器,使得无法建立 P2P 连接的客户端(NAT 严格限制导致)也能实现通讯。
TURN 协议的工作流程如下:
客户端发送 Allocate request 到 server,server 返回 401 未授权错误(带有 realm 和 nonce),客户端再发送带上认证信息的 Allocate request,server 返回成功分配的 relay address。分配成功后,客户端需要通过发送机制(Send Mechanism)或信道机制(Channels)在 server 上配置和其他 peer 的转发信息。此外 allocation 和 channel 都需要保活。
WebRTC 使用的是信道机制,因为这一机制的数据开销更低。
收集 TURN relay candidate 时也可以复用收集 host candidate 时创建的 socket 对象,这一逻辑通过 PORTALLOCATOR_ENABLE_SHARED_SOCKET
flag 控制,前面我们就已经知道,默认情况下它是开启的。
由于 TURN 协议是 STUN 协议的扩展,所以基本的发送请求、接收响应的代码是复用的,下面只描述 TURN 协议独特的部分:
- AllocationSequence::OnMessage
- AllocationSequence::CreateRelayPorts
- BasicPortAllocatorSession::AddAllocatedPort
- TurnPort::PrepareAddress
- TurnPort::SendRequest
- ↓ message
- StunRequest::OnMessage
- ↓ 发送请求、接收响应
- StunRequestManager::CheckResponse
- TurnAllocateRequest::OnErrorResponse
- TurnAllocateRequest::OnAuthChallenge
- TurnPort::SendRequest
- ↓ 发送请求、接收响应
- StunRequestManager::CheckResponse
- TurnAllocateRequest::OnResponse
- TurnPort::OnAllocateSuccess
- Port::AddAddress
- ↓ sig slot (SignalCandidateReady)
- BasicPortAllocatorSession::OnCandidateReady