随笔

WebRTC

WebRTC 可以实现浏览器之间点对点的连接,而不需要经过服务器传输,所以从安全性及隐私性,以及数据传输速度都是更好的解决方案,当然单纯传输速度来讲并不一定提高,因为用户网络环境复杂,大的通讯商可以借助专有骨干网络通讯,而点对点可能需要一层层中转。

Preview

预览效果如下图(高糊,反正陌生人认不出,认识的一眼就认识),左侧横向的是电脑,右边纵向的是手机。

preview.png

Connect Method

众所周知,网络环境非常复杂,很难通过一个 IP 定位到设备,尤其是国内各种 NAT 映射到城域网,根本不可能通过 IP 找到,所以就需要一些协议来发现设备,点击此链接查看所有协议。

这里介绍一种比较通用的 STUN 协议,STUN Server 一般被称为信令服务器,帮助设备之间的互相发现,信令服务器代码很简单,所以资源消耗也很低,可以采用公开的服务,也可以自己搭建,类似于一个 DNS 解析器。

How Work

如图中,A 和 B 用户去问 STUN Server 我在哪里?分别得到了在哪里的信息后,就可以进行点对点的连接了,啊当然这里肯定是经过一个普通的 API Server 去中转的,不然就是黑盒了。

就像战斗机飞行员,先要通过指挥中心得到前线部队的通讯编号,然后才能通过无线电加入前线部队频道。

webrtc-stun.png

Establish connection

Initial Client

const socketInfo = io.connect("https://note.lilonghe.net", {
  path: "/test/webrtc/api/socket.io/"
});

const peerConnection = new RTCPeerConnection({
  iceServers: [
    {
      urls: "stun:stun.l.google.com:19302",
    },
  ],
});

Connect Camera And Listen Remote

navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then(stream => {
  stream.getTracks().forEach(track => {
    peerConnection.value.addTrack(track, stream);
  });
});

peerConnection.ontrack = (ev) => {
  if (remoteVideoElement) {
    remoteVideoElement.srcObject = ev.streams[0];
  }
};

Initiate A Communication

这里代码中的 SDP 即会话的协议描述,里面包含很多信息,通过这个 SDP 就可以得到连接对方的协议及加密算法,用于后续连接时怎么处理对方的数据。

const sdp = await peerConnection.value.createOffer({
  offerToReceiveAudio: true,
  offerToReceiveVideo: true,
});
await peerConnection.value.setLocalDescription(new RTCSessionDescription(sdp));
socketInfo.emit("offer", sdp);

Answer Communication

await peerConnection.setRemoteDescription(new RTCSessionDescription(sdp));
const mySdp = await peerConnection.createAnswer({
  offerToReceiveVideo: true,
  offerToReceiveAudio: true,
});
await peerConnection.setLocalDescription(new RTCSessionDescription(mySdp));
socketInfo.emit("answer", mySdp);

Establish Communication

一个是己方准备完毕发送出去,一个是监听对方准备完毕

peerConnection.onicecandidate = (e) => {
  if (e.candidate) {
      socketInfo.emit("candidate", e.candidate);
  }
};

socketInfo.on("getCandidate", async candidate => {
    await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
});

candidate.png

可以很明显的看到,其中包含了寻址信息。

Communication Establishment Process

这里展示发起方和应答方和 API Server 沟通的过程。

发起方
发起方.png

应答方
应答方.png

Deploy

至此,通讯创建完成,接着就是部署到服务器上,因为用到了 WebSocket,所以需要配置 Nginx。开发时如果是本机 IP,比如 localhost 是可以调试的,但如果不是本机 IP,就需要支持 HTTPS 的服务器。

以下是简单配置支持,正式部署还需要根据环境进行优化。

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

location /test/webrtc/api/ {
    proxy_pass    http://localhost:8088/;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}

Reference

本文链接:https://note.lilonghe.net/post/hello-webrtc-demo.html

-- EOF --