一、方案选型
1、XMPP协议 + Jingle扩展协议:应用层协议,可以用来实现音视频传输,但完善的客户端开发资料有限,无开源库,开发难度大,开发周期长。(选型排名第三)
2、SIP协议:应用层协议,专门用来实现音视频传输,但客户端开发资料较少,无开源库,开发难度较大,开发周期较长。(选型排名第四)
3、RTMP协议(Real Time Messaging Protocol,实时消息传输协议):传输层协议,基于TCP实现,是目前主流的音视频等流媒体传输协议,广泛应用于直播领域,开发资料较全,但是传输成本高,对服务器要求较高。(选型排名第二)
4、WebRTC(Web Real-Time Communication,网页即时通讯):本身并不是什么协议,而是谷歌的一套开源库,专门用来做点对点音视频通信,应用层采用的是XMPP协议的扩展组件libjingle,传输层采用UDP协议,采用ICE/STUN协议进行NAT穿透。协议分层如下
优势:
-
可实现Web浏览器、安卓、iOS多端通信。
-
客户端难的地方主要体现在两个方面,一是网络传输有关,像侦听事件,同步主线程和读线程,穿透;二是流数据有关,像音视频编码、解码、回声消除等;而这两大问题WebRTC内部已经帮我们解决了。
现在许多SDK都使用WebRTC作为框架来实现,开发资料较完善。(选型排名第一)
二、WebRTC的架构模式
上面我们说到:WebRTC本身并不是什么协议,而是谷歌的一套开源库,专门用来做点对点音视频通信,应用层采用的是XMPP协议的扩展组件libjingle,传输层采用UDP协议,采用ICE/STUN协议进行NAT穿透。
1、点对点模式
传统的连接模式一般都是C/S架构(Client/Server架构)的,例如XMPP,A和B之间互相发送数据的流程是:A–>服务端–>B,B–>服务端–>A。
而WebRTC则是P2P模式(peer-to-peer模式)的,即点对点的,也就是说,一旦点与点之间的连接形成,那么它们之间的数据传输是不经过服务端的,而是在两者之间直接发送。如:A–>B,B–>A。这样做的一个很大的好处就是大大的降低了服务端的压力。
2、服务端、信道与信令
那这是否就意味着我们使用WebRTC开发就不需要服务端了呢?这种观点很明显是错误的,严格来说,我们使用WebRTC仅仅是不需要服务端来进行数据中转,服务端还是必不可少的,虽然服务端会比C/S架构下少做很多事,但还是不能没有它。使用WebRTC至少要用服务端做两件事:
-
像
A-->服务端-->B
这样的模式来传递一些基本信令来控制P2P连接的连接与断开,还有一些自定义业务的信令,也就是说服务端的主要作用其实就是在两个客户端之间传递信令。信令其实就是一些指令型的数据,而客户端与服务端之间传输信令通道就是信道,我们用websokcet搭建长连接作为信道。 -
穿越NAT。
什么是NAT?
NAT(Network Address Translation)即网络地址转换,NAT技术的出现,就是为了解决IPV4下IP地址匮乏的问题。比如说,我们有很多设备处于同一个路由器下,这个路由器只有一个公网IP,但是通过NAT技术我们的设备都会被分配到相应的内网IP(192.168.0.1 、192.168.0.2等),这样一个路由器的公网IP就对应了n个内网IP,就通过这种使用少量的公有IP代表较多的私有IP的方式,减缓了可用的IP地址空间的枯竭。
为什么要穿越NAT?
但是这也带来了一系列的问题,例如这里点对点连接下,会导致这样一个问题:如果客户端A想给客户端B发送数据,则数据来到客户端B所在的路由器下,会被NAT阻拦,这样B就无法收到A的数据了。所以需要穿越NAT。
怎么穿越NAT?
两个客户端分别去谷歌的STU Server获取自己对应的公网IP,通过信令的方式经过服务端传递给对方挂在peerConnection上就可以了。
三、peerConnection的详细连接流程及信令传输
共7步,7个信令。
-
第一步:用户打开聊天室页面,客户端与服务端建立websocket连接,打通信道,此时服务端为该用户分配一个socketid作为WebRTC音视频通话的唯一标识。
-
第二步:加入聊天室信令
join
信道打通后,客户端需要向服务端发送一个加入聊天室信令join
,信令中要包含当前聊天室的唯一标识。
- 第三步:当前聊天室里所有用户信令
peers
,有新用户加入的信令_new_peer
服务端收到join
信令后,根据客户端加入的房间,发送一个当前聊天室里所有用户(包括当前客户端自己)的信令peers
给客户端,这个信令的一个作用就是服务端告诉客户端他加入聊天室成功了,二来也把当前聊天室里所有用户发送给客户端。此时,客户端会创建本地流,并与房间里的其它用户分别建立peerConnection连接,并把本地流添加到所有的P2P连接上。
加入聊天室成功,此聊天室没有人的情况
{
data = {
connections = (
);
you = "e297f0c0-fda5-4e67-b4dc-3745943d91bd";
};
eventName = "_peers";
}
加入聊天室成功,此聊天室有人的情况
{
data = {
connections = (
"85fc08a4-77cb-4f45-81f9-c0a0ef1b6949"
);
you = "4b73e126-e9c4-4307-bf8e-20a5a9b1f133";
};
eventName = "_peers";
}
同时服务端还需要发送有新用户加入的信令_new_peer
给聊天室里其它所有的人,要包含这个新人的socketid,用来让房间内所有其它用户跟这个建立新加入的人建立peerConnection,并把它们的本地流添加到这个连接上去。
这样,双方互相发起和对方的连接,经过WebRTC的处理,该连接建立成功,但是这个连接还不能正常传数据,还有两件事要做:
往这个连接上挂上双方的公网ip,用来穿越NAT。
往这个连接上挂上双方的sdp,用来描述这个连接传递的具体内容。
- 第四步:发送公网IP信令
__ice_candidate
我们在第三步建立每条peerConnection的同时,就会为每条peerConnection都发起向谷歌STUN服务器发起获取ice candidate的请求(直接理解为公网IP就行了),获取到后会触发我们的一个回调,当我们获取到这个公网IP后,要通过发送公网IP信令__ice_candidate
发送给服务端。
服务端收到A发送的ice candidate,也需要发送一个_ice_candidate
信令给房间其他所有的用户,让他们把A的公网IP挂在它们和A的peerConnection中。
同理,B在建立和A的peerConnection时也会去请求自己的公网IP,并经过服务端发给A,让A把B的公网IP关在它们俩的peerConnection上。
这样就完成了NAT穿越。
- 第五步:
offer
和answer
信令
那么当新加入的用户收到peers
信令并建立连接完成、并传输公网IP完成后,需要给聊天室里所有的人都发送offer
信令把自己的sdp传给他们,他们则要在收到offer
信令后回复我们一个answer
信令把它们的sdp传给我们,以此来完成sdp的互相存储。
早就在房间里收到newPeer
信令的用户不需要发offer
信令并等待answer
信令。sdp的交换已经有上面那一条传输线完成了。
offer和answer信令的作用都是传递sdp,只不过offer信令是发起方发送的,answer信令是接收方发送的,信令的主要内容都是sdp信息。
那么当我们建立了peerconnection,并完成了公网IP的发送与接收后,我们还需要完成sdp的发送与接收。
我们发送方需要先主动create一个Offer,Offer里包含着一个本地的sdp信息(创建offer,核心就是创建本地sdp,创建本地sdp成功后会触发一个回调,我们在回调里发信令),创建好后,我们要发送一个发送sdp的信令__offer
给服务端。
服务端同样通过__offer
信令再把这个sdp发给聊天室的所有其它用户,让他们把这个sdp挂在相应的peerConnection上。
其他用户收到服务端发来的A的sdp信息后,通过setRemoteDescriptionWithDelegate: sessionDescription:
方法把A的sdp信息存储在A和B的peerConnection中,设置成功后会触发一个代理方法,其它用户就要在这个代理方法里发出一个__ answer
信令把自己本地的sdp信息发送给服务端。
服务端同样通过__ answer
信令再把这个sdp发送给客户端A,A把这个sdp挂在相应的peerConnection上。
这样这个连接才算真正打通,可以把它上面挂着的流进行点到点的传输了。
- 第七步:有用户离开的信令
_remove_peer
当你退出聊天室时,你不需要发送信令,服务端可以知道是谁退出了,就好像刚开始知道是谁加入了一样,你只需要要关闭多媒体流,关闭和所有用户的连接,关闭和服务端的socket连接。
而服务端则给房间里其它用户发送一个有用户离开的信令_remove_peer
,让别人和你断开连接。
注意:
客户端和服务端的连接必须为长连接,你用啥协议实现都行,但是必须是长连接,因为在一个聊天存在时客户端和服务端会反复传输数据。(这个长连接就是信道,传输的数据就是信令)
ice candidate
其实就是客户端的公网地址,用来进行NAT穿透。
SDP
就是一个描述多媒体连接内容的协议,例如分辨率,格式,编码,加密算法等,通信双端要一致。双端之间的连接和信令传输是本次实现的重难点。
作者:意一ineyee
链接:https://www.jianshu.com/p/813ccde94908
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。