|
沙发

楼主 |
发表于 2017-1-7 21:38:50
|
只看该作者
本帖最后由 ancientcc 于 2017-1-10 11:37 编辑
图1:和网络连接相关的数据结构
建立呼叫要交换两种信息,一是候选地址,二是媒体信息。候选地址用于建立网络连接,它存储着和网络连接相关的参数。媒体信息用于描述要在对等连接上传输的数据,包括音频、视频和数据。用路和车来比喻的话,候选地址用于造路,媒体信息于用指定要跑什么车。
几条结论
- 候选地址相关工作是在SetLocalDescription时启动,在这之前甚至没有任何网络传输。
- 交换候选地址和交换Offer、Answer是并发进行。为什么呢?它们都须要进行网络传输,一旦涉及到网络传输,需要的时间就不可预测,为良好UI须要采用并发。
- 交换Offer、Answer用的是信令通道。为什么要强调这个?有人认为建立网络连接和Offer、Answer无关,因而理论上可以等网络通畅后用A、B之间的对等连接传Offer、Answer,也就是说可以不通过信令通道。——以上认为是对的,但还是为要有好的UI,加之Offer/Answer数据量不大,还是不要等对等连接通畅后再传媒体信息。
- 为实现并发,webrtc内部有三个相关线程,signaling_thread、worker_thread和network_thread。signaling_thread不是webrtc创建,它就是app主线程,后面两个是webrtc创建的线程,worker_thread用于处理和媒体相关的Offer、Answer,network_thread则用于候选地址。
深入候选地址
在控制台,常能看到类似以下的字符串。
- Local candidate{"candidate":"candidate:1289435463 1 udp 2122260223 10.1.1.19 64462 typ host generation 0 ufrag TWCy network-id 2 network-cost 50","sdpMid":"sdparta_0","sdpMLineIndex":0}
- Local candidate{"candidate":"candidate:240568271 1 udp 1686052607 174.139.8.82 64462 typ srflx raddr 10.1.1.19 rport 64462 generation 0 ufrag TWCy network-id 2 network-cost 50","sdpMid":"sdparta_0","sdpMLineIndex":0}
复制代码
大括号中字符串格式
- candidate:{foundation} {component} {protocol} {priority} {ip} {port} typ {type}
- 如果存在related ip:raddr {ip} {port}
- generation {generation}
- 如果存在用户名:ufrag {username}
- 如果network_id不是0:network-id {network_id}
- network-cost {network_cost}
复制代码
在说字段意义前让回看图1给出的网络连接涉及到的数据结构。PeerConnection常简写为pc,一次会话会首先建立它,每个pc包含一个WebRtcSession类型的session_。session_中有两种成员,一是TransportController类型的transport_controller_,二是从BaseChannel派生的轨道。当app只传音频时,那只有VoiceChannel有效,transport_controller_中的transports_只有一个tag是“audio”的单元,当要同时传音、视频时,transports_有两个单元,tag分别是“audio”和“video”。channels_是轨道中成员,表示为实现该轨道须要开辟的通道。通道是什么呢?它和传输协议有关,webrtc传输媒体数据用的是RTP协议,在这里一个通道对应或是RTP或是RTCP,也就意味着一轨道有两通道。通道下有本机上的网卡集合:Ports_,一个网卡被抽像成一个从Port派生的类,像UDPPort,对网卡,可理解为等同IP,这个IP可以是ipv4或ipv6。举个例子,本地有一个wifi、一个vpn、一个遂道适配器,那就有三个网卡,即Ports_长度是3。网卡底下有Connection集合,Connection表示一个本地端口和远程端口之间的通信链路,更多的底下会说。
每个通道都含有本地所有的网卡,后面逻辑会选定这个通道要基于哪个网卡。
知道了轨道、通道、网卡、Connection,让回头分析以下字符串中大括号中各字段意义。
- Local candidate{"candidate":"candidate:240568271 1 udp 1686052607 174.139.8.82 64462 typ srflx raddr 10.1.1.19 rport 64462 generation 0 ufrag TWCy network-id 2 network-cost 50","sdpMid":"sdparta_0","sdpMLineIndex":0}
复制代码
- foundation(240568271):根据type、ip、protocol、replay_protocol计算出的字符串。一般于比较两个candidate是否相等。
- component(1):通道码。RTP通道码是1、RTCP是2,它指示这候选地址关联RTP通道。
- protocol(udp):传输层类型,upd或tcp。往往认为RTP、RTCP是用UDP,但webrtc其实支持用TCP。
- priority(1686052607):<尚未理解>
- ip(174.139.8.82):候选IP。它是真正须要的候选地址。当type是反射时,它就是NAT外的公网IP,此时raddr对应内网IP,
- port(64462):候选IP关连的端口号。
- type(srflx):候选地址类型。它分本地(local)、反射(srflx),中转(relay)。
- raddr(10.1.1.19):候选IP基于的IP。对于local类型,它不存在。是反射时,它就是内网IP。
- rport(64462):raddr关联的端口。
- generation(0):代数。初始值是0,然后会不断+1,大的代数会覆盖掉低代数的候选地址。
- ufrag(TWCy):用户名。
- network-id(2):此网卡IP在网卡集合中的索引,从1始。
小结关于候选地址的FAQ
A:什么时候开始?
Q:SetLocalDescription结束前会调用MaybeStartGathering,它开始搜集候选地址。
A:在哪线程收集?
Q:network_thread_。
A:须要多少个端口(测试连接数)?
Q:端口数=轨道数*(RTCP+RTP)*本地可用网络适配器数。假设要同时传语音和图像、本地有三个可用适配器,那须要分配12个端口,意味着须要12个AllocationSequence对象。创建端口后就立即向stun服务器发12个请求包,以确定对应的公网IP:port(UDPPort::MaybePrepareStunCandidate)。
A:HOST地址的收集方法?
Q:本机网卡IP。对端通过信令服务器得到。
A:服务器反射地址(srflx)的收集方法?
Q:向stun服务器发Binding Reuest,从应答解析出的地址。对端通过信令服务器得到。
A:对等端反射地址(prflx)?
Q:stun检查时解析出的地址,地址就是该Stun Binding Reuest数据包来自的那个IP:port。对端不通过信令服务器得到。
A:中继地址的收集方法?
Q:向turn服务器发Allocate Request,然后解析响应得到的中继地址(XOR-RELAYED-ADDRESS)。对端通过信令服务器得到。
A:如何指定stun、turn的服务器IP:port?
Q:CreatePeerConnection的第一个参数RTCConfiguration中的servers字段。stun服务器必须以stun为前缀,像“stun:stun.l.google.com:19302”,turn服务器必须以“turn”为前缀。
A:什么时候收集结束?
如果要确定结束标准,那只能针对每个通道。通道何时收集结束?我还未完全清楚,但应该和Connection状态有关。
A:什么是绑定(bundle)
为什么要绑定?要同时传视频、音频,意味着要使用两个端口,两个端口不仅占资源,还增加网络不可靠,绑定目的就是要让一个端口发送视、音频。
SDP内容决定了如何使用绑定,以下是和绑定相关字段。
- a=group:BUNDLE sdparta_0 sdparta_1
- m=audio .....
- a=mid:sdparta_0
- m=video ......
- a=mid:sdparta_1
复制代码 会话级别属性a=group:BUNDLE指示此ICE代理支持绑定。后面的sdparta_0、sdparta_1指示了要绑在一块的媒体流,这个名称是自定义的,但后面必须有对应的a=mid属性。上例中sdparta_0对应audio,sdparta_1对应video,指示把audio、video流绑在一个端口。
Conection
Connection表示一个本地端口和远程端口之间的通信链路。一旦收到一个远程候选地址,找到该地址对应的通道,会在通道下多卡网卡建立Connect!举个例子,本地有一个wifi(ipv4)、一个vpn(ipv4)、一个遂道适配器(ipv6),即通道下有三个网卡,它枚举这三个网卡,如果1)协议、2)AF一致,那会在该网卡上创建Connection。以这个例子说,如果收到的候选地址是ipv4,就会在该通道下的wifi、vpn创建Connection。在stun检查时,会基于这两个Connection向对方发请求包。
在数据结构上,Connection被Port(网卡)所有,它的port_成员指向这个Port。以下是Connection在Port的存放结构:
- std::map<rtc::SocketAddress, Connection*> connections_;
复制代码
first是远程端口地址。为什么网卡中的Conection是个集合?如果对方在指定通道有三个候选地址,像本地、反射、中转,那这个集合中就有三个单元,至于最终传输用的是哪个单元,那应该有算法,这个我<尚不清楚>。
何时创建Connection?Stun检查时被创建。对此Port来说,Stun检查就是它收到了对等端发来的一个Binding Request。是第一个Request时,会为此创建Connection。作为Stun检查后续,要用该Connection::HandleBindingRequest回应此个绑定的应答。是第二或更后面Request时,Port已存在此Connection,但还是要用HandleBindingRequest发回应答。举个例子,Port的IP:port是192.168.1.105:59042,它收到一个来自192.168.1.104:60002的Request,那它会创建一个Connection,Port中的connections_将有以下条目。
- 192.168.1.104:6002 ===> Connection1
复制代码
如果此Port还收到从另一个172.16.40.6 60059收到Binding Request,那之后connections_将有两个条目。
- 192.168.1.104:6002 ===> Connection1
- 172.16.40.6:60059 ===> Connection2
复制代码
深入Offer、Answer
(此处和C/C++代码结合较深,只有看过代码才可能理解)
WebRtcSessionDescriptionFactory::certificate_request_state_:指示证书请求态状,初始值是CERTIFICATE_NOT_NEEDED。
RTCCentificate用于描述证书。它可以由CreatePeerConnection时在参数中指定,但一般app都不指定。由于没有指定,在WebRtcSessionDescriptionFactory的构造函数时certificate_request_state_就改到CERTIFICATE_WAITING,指示在等待证书。一旦外界指定了,还是会改到CERTIFICATE_WAITING,不同的是会立即调用signaling_thread_->Post。
- signaling_thread_->Post(RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE, new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
复制代码
证书其实在WebRtcSessionDescriptionFactory构造函数期间就已生成。在构造期间,发现外部没有指定证书,就调用GenerateCertificateAsync,后者会Post给worker_thread_消息MSG_GENERATE,由于构造线程是singaling_thread_,只要发生线程调度,就会调用worker_thread_,然后执行处理MSG_GENERATE消息,它的功能就是产生证书,产生证书后,它会向signaling_thread_投递MSG_GENERATE_DONE消息。但由于signaling_thread_一直在处理初始化操作,不会处理消息,一直要到执行到初始化完成,即PeerConnection::CreateOffer,CreateOffer会构一个offer请求,并把请求放入create_session_description_requests_。
接下signaling_thread_要处理worker_thread_投递给它的MSG_GENERATE_DONE。以下是处理逻辑。
- 把certificate_request_state_置为CERTIFICATE_SUCCEEDED。
- 逐个处理create_session_description_requests_中请求,是Offer的调用InternalCreateOffer,是Answer是调用InternalCreateAnswer,生成完整的Offer/Answer。要注意,这两函数结束前会向signaling_thread_投递MSG_CREATE_SESSIONDESCRIPTION_SUCCESS。
signaling_thread_处理完MSG_GENERATE_DONE,它就处理MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,WebRtcSessionDescriptionFactory默认对它处理是调用CreateSessionDescriptionObserver的OnSuccess这个虚函数,app需重载这函数,至少要把这个Offer发送向信令服务器。
|
|