WebRTC对等通信-在不同设备上连接浏览器
我们成功了!不需要服务器即可使不同设备上的两个浏览器相互交流,只需要在开始交互的时候使用服务器。
点击此处运行代码,在不同的设备上打开两个链接。
如何使用WebRTC连接不同设备上的浏览器
你可以使用WebRTC在无服务器时使浏览器相互交流,但是由于没有服务发现,我们需要一个发信服务器来使浏览器可以找到彼此。
步骤如下:
1.客户端1 对服务器说hi并注册。
2.客户端2 对服务器说hi并注册。
3.服务器记录身份信息(用户名)
4.客户端1命令服务器呼叫客户端2
5.服务器告诉客户端2有呼叫信息。
6.客户端2接收呼叫。
7.客户端1和2直接交流。
我们模仿MDN的WebRTC交流实例来形成代码框架。
WebSocket信令服务器
发信号是两个浏览器之间的交互过程,我们使用了WebSockets来完成。
服务器部分和MDN的例子相同,只需复制。
我们做出了一些改变使它可以对now.sh项目工作。名称上,我们移除了所有SSL部分。Zeit将我们的服务器包进一个安全的SSL 服务器,SSL服务器接着通过一个非加密连接与实际服务器进行交流。
如果没有SSL,WebSockets不能在现代浏览器中起作用。如果没有运行本地主机,它对自签名证书不工作。如果需要两个不在本地设备上的浏览器交流,必须确保一个SSL证书。
最简单的方法是在now项目上进行。
连接信令服务器
通过40行helper类的WebSockets可以与服务器交流。实例化类,建立连接,收听信息。
我们在connetToSocket中建立了一个new WebSocket,加入一些反馈,期待好的效果。Onmessage允许我们之后通过messageListeners数组添加额外的message listeners 窗口。
SendToServer 允许向服务器发送JSON对象,并且addMsgListener允许添加一个新的message listener 窗口。 我们使用它来连接PeerConnection 和服务器。
建立PeerConnection接口
从WebRTC part1 可以学到, 我们把RTCPeerConnection组件分成了help class。
以下148行代码完成了整个周期。我们之前讨论过此代码,这是重述。
Constructor建立了一些实例变量,一个新的RTCPeerConnection对象,告诉它用哪个iceServers,连接本地event listeners,然后开始接受信令服务器信息,并添加媒体流到peerConnection.
下一步是处理ICECandidate,当有新连接时它将建立一个的交互连接。它会ping信令服务器,告诉信令服务器,这里有新的ICE candidate.
handleICECandidateEvent = event => { if (event.candidate) { this.signalingConnection.sendToServer({ type: "new-ice-candidate", target: this.targetUsername, candidate: event.candidate }); } };这之后,我们得到了
handleNegotiationNeededEvent,当RTCPeerConnection需要新连接时,它会被调用。(我也不知道是什么触发了它)但是函数产生了一个新的连接请求,更新了本地SDP描述,并且告知信令服务器它在尝试连接。
handleNegotiationNeededEvent = () => { const { username, targetUsername } = this; this.peerConnection .createOffer() .then(offer => this.peerConnection.setLocalDescription(offer)) .then(() => this.signalingConnection.sendToServer({ name: username, target: targetUsername, type: "video-offer", sdp: this.peerConnection.localDescription }) ) .catch(console.error); };处理信令消息
接着到了有趣的部分,处理信令服务器的消息。
onSignalingMessage = (msg) => { switch (msg.type) { case "video-answer": // Callee has answered our offer this.videoAnswer(msg); break; case "new-ice-candidate": // A new ICE candidate has been received this.newICECandidate(msg) break; case "hang-up": // The other peer has hung up the call this.close() break; } }当接收新信息时,我们可以做许多事。把我们自己设置成接收方,向连接中添加新的candidate或关闭。
videoAnswer = ({ sdp }) => { this.peerConnection .setRemoteDescription(new RTCSessionDescription(sdp)) .catch(console.error); } newICECandidate = ({ candidate }) => { this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); } close = () => { this.peerConnection.close(); this.peerConnection = null; this.onClose() }这就是我们的PeerConnection对象。理论上,我们可以实例化许多PeerConnection对象来同时连接远端机器。
这将会是一个有趣的尝试。
将所有部件整合
把这些组合起来的,就是我们的
WebRTCPeerConnectionWithServer反应组件,经过用户界面,实例化helper类,处理用户点击按钮的过程。点击此处查看GitHub完整文件。
以下是其中比较重要的部分:
call = user => { this.setState({ targetUsername: user }); this.createPeerConnection(); }; hangUp = () => { this.signalingConnection.sendToServer({ name: this.state.username, target: this.state.targetUsername, type: "hang-up" }); this.peerConnection.close(); }; createPeerConnection = () => { if (this.peerConnection) return; this.peerConnection = new PeerConnection({ gotRemoteStream: this.gotRemoteStream, gotRemoteTrack: this.gotRemoteTrack, signalingConnection: this.signalingConnection, onClose: this.closeVideoCall, localStream: this.state.localStream, username: this.state.username, targetUsername: this.state.targetUsername }); }; closeVideoCall = () => { this.remoteVideoRef.current.srcObject && this.remoteVideoRef.current.srcObject .getTracks() .forEach(track => track.stop()); this.remoteVideoRef.current.src = null; this.setState({ targetUsername: null, callDisabled: false }); };函数从call开始启动,保存我们正在调用的状态,建立一个新的peer连接。
CreatePeerConnection把所有信息传入PeerConnection类。
HangUp和closeVideoCall共同工作来停止调用。我们需要两者因为其中一个是用户控制的,而当挂断命令从另一边传来的时候,另一个被调用。
最后一步
在粘合区有一条信令服务器发出的信息需要我们处理: 调用请求。
case "video-offer": // Invitation and offer to chat this.createPeerConnection(); this.peerConnection.videoOffer(msg); break;当服务器告知需要连接时,我们需要在客户端创建一个新的PeerConnection对象,并处理请求。处理请求意味着设置一个远程SDP描述并发送应答。
videoOffer = ({ sdp }) => { const { username, targetUsername } = this; this.peerConnection .setRemoteDescription(new RTCSessionDescription(sdp)) .then(() => this.peerConnection.createAnswer()) .then(answer => { return this.peerConnection.setLocalDescription(answer); }) .then(() => { this.signalingConnection.sendToServer({ name: username, targetUsername: targetUsername, type: "video-answer", sdp: this.peerConnection.localDescription }); }) .catch(console.error); }成功工作
将所有部分结合,现在你可以在不同设备的两个浏览器上相互交流而不再需要服务器。
相关标签: