WebRTC candidate

一. 前言

        WebRTC 音视频通信的双方需要知道对端的通信地址才能进行通信,WebRTC 采用 ICE 方式与通信对端建立通信连接,ICE 中很关键的一个步骤就是收集候选者信息,本端需要将自己的主机候选者,NAT 映射后的候选者以及中继候选者等信息发送给对端,对端也需要发送候选者信息给到本端,双方进行媒体连通性检测,检测成功后才能进行通信。

二. candidate

        候选者也叫 candidate,它包含一个网络地址信息,有主机候选者(host candidate),服务器反射候选者(srflx candidate),中继候选者(relay candidate)等,各种候选者代表的含义如下。

        主机候选者(Local Address)是本地使用的 IP 地址和端口,例如通过 ifconfig/ipconfig 查看到 WLAN 网卡的 IP 地址是 192.168.0.105,并且准备使用 51417 端口。

        服务器反射候选者(Server Reflexive Address)是 NAT 映射后使用的 IP 地址和端口。

        中继候选者(Relayed Address) 是 TURN 服务器开辟的 IP 地址和端口,TURN 服务器是用来应对当通信对端 NAT 穿越失败无法建立 P2P 通信时,通过 TURN 服务器转发数据包,TURN 服务器一般都部署在带公网 IP 的机器上。

candidate 格式为:a=candidate:{foundation} {component} {protocol} {priority} {ip} {port} typ {type} generation {generation} ufrag {username} network_id {id} network_cost {cost}

以如下 candidate 为例说明其代表的含义:

a=candidate:1221703924 1 udp 2122260223 192.168.0.105 51417 typ host generation 0 ufrag Q8Wv network-id 1 network-cost 10

typ host 表示本地候选者,使用的 IP 是 192.168.0.105,端口为 51417,使用 UDP 协议,其优先级为 2122260223,generation 表示代数,初始值为 0,如果更新 candidate 则 generation 值会递增,替换老的 candidate。

        WebRTC example 提供了一个可以收集 candidate 的工具,通过该工具我们可以收集 Local Address 和 Server Reflexive Address,如果你搭建了 TURN 服务器,将 TURN 服务地址添加到 ICE Servers 中,你还可以收集到 Relayed Address。

三. WebRTC收集candidate源码剖析

        当本地通过 createOffer 生成 local SDP 后会再调用 setLocalDescription 设置到本地描述信息中,setLocalDescription 中就会进行 candidate 收集。

        如下当 PeerConnection::DoSetLocalDescription 完成 ApplyLocalDescription 后会执行 transport_controller_->MaybeStartGathering() 开始 candidate 收集。

void PeerConnection::DoSetLocalDescription(
    std::unique_ptr<SessionDescriptionInterface> desc,
    rtc::scoped_refptr<SetSessionDescriptionObserver> observer) {
  // 省略...(详见src/pc/peer_connection.cc)
  error = ApplyLocalDescription(std::move(desc));

  // 省略...

  // MaybeStartGathering needs to be called after posting
  // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates
  // before signaling that SetLocalDescription completed.
  transport_controller_->MaybeStartGathering();

  // 省略...
}

        如果当前线程不是网络线程,则向网络线程发送 MaybeStartGathering 的任务,否则执行 dtls->ice_transport()->MaybeStartGathering() 的逻辑。

        MaybeStartGathering 逻辑如下,如果当前状态为未收集过 candidate 的状态或者准备重启 ICE 的状态,则进入正在收集的状态,并且往 allocator_sessions_ 添加一个 session(该 session 是一个 PortAllocatorSession 对象),然后调用 session 的 StartGettingPorts。

 

        调用 StartGettingPorts 后的驱动流程为:GetPortConfiguarations -> OnConfigReady -> OnAllocate -> OnAllocationSequenceObjectsCreated。

 

 

        GetPortConfiguarations 首先创建 PortConfiguration 对象 config,然后往 config 对象添加 stun_servers,turn_servers 地址信息。

 

        OnConfigReady 则是将 PortConfiguration 对象塞入 configs_ 中。

 

 

        OnAllocate 中调用了 DoAllocate,DoAllocate 中最重要的逻辑就是为每个 Network 生成一个 AllocationSequence 对象(如果你对 Network 对象不太熟悉,可以先阅读这篇博客了解 WebRTC 收集网卡信息的流程)。

        DoAllocate 中使用 Network, PortConfiguration 以及 sequence_flags 创建 AllocationSequence 对象后,会再为 AllocationSequence 对象执行 Init 初始化。 

        AllocationSequence::Init 调用 BasicPacketSocketFactory::CreateUdpSocket 创建出 socket,该 socket 绑定了 Network 对应的 ip 地址以及从 [min_port, max_port] 范围挑选的一个可用端口,socket 创建的调用流程如下。

         AllocationSequence::Init 执行完成后会再调用 AllocationSequence::Start,该函数主要是给网络线程发送 MSG_ALLOCATION_PHASE 消息,驱动 UDPPort,TcpPort,StunPort,RelayPort 等对象的创建。

        我们以 CreateUDPPorts 为例说明 host candidate 是如何收集的,对于收集服务器反射地址候选者请阅读这篇博客,对于收集中继地址候选者请阅读这篇博客

        CreateUDPPorts 逻辑如下,首先是创建出 UDPPort 对象,然后通过 BasicPortAllocatorSession::AddAllocatedPort 添加 UDPPort 进行处理。

        BasicPortAllocatorSession::AddAllocatedPort 主要是为 port content_name, component 等属性,并关联一些事件的回调处理函数,然后执行 PrepareAddress。

        PrepareAddress 调用了 OnLocalAddressReady,OnLocalAddressReady 又调用 AddAddress 创建 candidate。

 

        至此本地候选者即创建完毕,对于服务器反射地址候选者的收集请阅读这篇博客,对于中继地址候选者的收集请阅读这篇博客,候选者信息创建完成后会给网络线程发送 MSG_SEQUENCEOBJECTS_CREATED 消息,回调 OnAllocationSequenceObjectsCreated 候选者创建完成。

四. 参考资料

RFC5245

RFC8445

RFC5766

猜你喜欢

转载自blog.csdn.net/weixin_38102771/article/details/123193845