WebRTC:P2P 连接过程完全解析

        宏观流程:

  • 设置 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: 

  1. PeerConnection::SetLocalDescription
  2. JsepTransportController::MaybeStartGathering
  3. P2PTransportChannel::MaybeStartGathering
  4. BasicPortAllocatorSession::StartGettingPorts
  5. BasicPortAllocatorSession::DoAllocate

        DoAllocate:

        DoAllocate 里会遍历所有网络设备(Network 对象),创建 AllocationSequence 对象,调用其 Init Start 函数,分配 port。 

  1. BasicPortAllocatorSession::DoAllocate
  2. AllocationSequence::Start
  3. 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

  1. AllocationSequence::OnMessage
  2. AllocationSequence::CreateUDPPorts
  3. BasicPortAllocatorSession::AddAllocatedPort
  4. UDPPort::PrepareAddress
  5. UDPPort::OnLocalAddressReady
  6. Port::AddAddress
  7.  sig slot (SignalCandidateReady)
  8. 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 协议独特的部分:

  1. AllocationSequence::OnMessage
  2. AllocationSequence::CreateRelayPorts
  3. BasicPortAllocatorSession::AddAllocatedPort
  4. TurnPort::PrepareAddress
  5. TurnPort::SendRequest
  6. ↓ message
  7. StunRequest::OnMessage
  8. 发送请求、接收响应
  9. StunRequestManager::CheckResponse
  10. TurnAllocateRequest::OnErrorResponse
  11. TurnAllocateRequest::OnAuthChallenge
  12. TurnPort::SendRequest
  13. 发送请求、接收响应
  14. StunRequestManager::CheckResponse
  15. TurnAllocateRequest::OnResponse
  16. TurnPort::OnAllocateSuccess
  17. Port::AddAddress
  18. ↓ sig slot (SignalCandidateReady)
  19. BasicPortAllocatorSession::OnCandidateReady

你可能感兴趣的:(webrtc,p2p,webrtc,网络协议)