基于https://www.bilibili.com/video/BV1Wi4y1N74y
标题纯原生JS的意思就是,不含任何后端代码,服务器搭建等操作,用最纯粹的方式实现
主要原理
关于WebRTC的原理,我的白话理解就是,两个客户端,创建自己的地址信息(SDP),然后通过某种方式两者交换一下记录对方的地址, 就能直接建立端到端的通信通道了。
由于通信过程中不用经过服务器中转,所以对服务器的负荷压力小,但是在多端交互时一个客户端要将一份数据复制多份同时传输,客户端的带宽可能不够,总之是有好有坏的。但是鉴于其不错的延迟和未来ipv6的支持,我感觉这个技术还有很多潜力。
在实际使用中,一般由一个端发送出offer,通过服务器(一般用websocket)送给另一个端,后者返回一个answer给发送端,offer和answer就是两端的SDP,互相记录一下就可以开始通信了。
发送端和接收端是平等的,一般可以进入房间的人给每一个人发一个offer,或是每进一个人,房间中每个人给他发一份offer
JS代码原理演示
发送端创建自己的SDP:
所谓iceCandidate
就只是说明具有SDP了而已,当各个端将自己的SDP设置成自己的本地地址setLocalDescription
后,就是成为一个iceCandidate
了
下面的代码有很多匿名式的写法,大概理解一下,其实格式差不多照抄就行
//source
const senderP = new RTCPeerConnection();
//打开一个数据通道,并设置打开和接收到消息的处理函数
const dc = senderP .createDataChannel("channel");
dc.onmessage = e => console.log("Got a message:" + e.data);
dc.onopen = e => console.log("Connection opened!")
//设置当成为ICEcandidate后,打印自己的地址
senderP.onicecandidate = e => console.log("New Ice Candidate! My SDP : " + JSON.stringify(senderP.localDescription))
//当createOffer后,将offer设置到本地Description中,此时自己就成为了一个iceCandidate,便触发地址打印函数
senderP.createOffer().then(o => senderP.setLocalDescription(o) ).then( a => console.log("set successfully!"))
如图,此时就打印出来了一个SDP,也就是发送端要发送出去的offer,
现在是发送端的本地地址,要设置为接收端的远程地址
我们把它记录下来,打开一个新的窗口,创建一个新的端。这个端用来接收
//首先把发送端送来的offer记录一下
let offer = {xxxxxxxxxxxxxx}
//destination
const receiveP = new RTCPeerConnection();
当出现ICEcandidate后,打印自己的地址
receiveP.onicecandidate = e => console.log("New Ice Candidate! My SDP : " + JSON.stringify(receiveP.localDescription))
//当接收到数据通道后,记录到本地。并设置打开和关闭的消息处理函数
receiveP.ondatachannel = e => {
receiveP.dc = e.channel;
receiveP.dc.onmessage = e => console.log("new message from client!" + e.data);
receiveP.dc.onopen = e => console.log("Connection Opened!")
}
//当接收到对方的offer信息后,设置到本地连接的RemoteDescription中
receiveP.setRemoteDescription(offer).then(a => console.log("offer set!"))
//后创建应答,并将应答设定为本地Description中,此时自己也就成为了一个iceCandidate,获取到自己的地址
receiveP.createAnswer().then (a => receiveP.setLocalDescription(a)).then(a => console.log("answer created"))
最后只需要将这个SDP(接收端创建的answer)发回去,设置为发送端的远程地址
这样两者的本地、远程地址就都有了
senderP.setRemoteDescription(answer)
可以直接从datachannel中发送信息
可以配置媒体流直接发送,不过以后再说,关于这里的P2P连接还有别的要做
STUN的NAT穿透
上面的方法只适用于没有NAT网关的情况下,也就是两台设备都拥有自己的公网IP,或者是同处于一个局域网下才可以实现连接
但是我们一般两台电脑都是在各自家里的路由器的管理下的,也就是对外只有NAT享受公网IP的快乐
所以需要通过STUN方案获取公网IP和局域网IP,
其原理是本机既然无法问NAT自己的地址是什么,那就向一个外部的服务器发送一个数据包,外部服务器就会帮忙解析出自己的地址并发回去,此时的SDP将同时具有公网IP和局域网IP的定位能力
TURN的保底连接
此外STUN也有可能失败,比如咱中国错综复杂的运营商、wifi/4g网络差异,有可能STUN定位失败,这是要通过TURN方案强行建立连接
,
其原理和普通服务器转发基本一样,带来巨大的服务器负载压力,所以还蛮难受的从对技术的情感上来看
STUN/TURN配置方法
配置方法是在创建RTCPeerConnection
时导入配置参数,构建对象时就会自动使用STUN/TURN
STUN服务器只是用来定位,有许多免费可用的,TURN服务器要求负载能力比较高,购买挺贵的应该,不过我是自己搭了一个用用就行
let configuration = {
'iceServers':[{
'url':'stun:stun.voipbuster.com:3478'
},
{
'url':'turn:49.235.xxx.xx',
username:'test',credential:'test'
}]
}
const peer = new RTCPeerConnection(configuration);