首页>>新闻资讯>>云计算

WebRTC对等连接(一):点对点通信

2024-10-09 09:20:15 2

WebRTC对等通信

在之前的工作中,我们使用了区块链技术来实时共享客户端模块,本次我们用RTCPeerConnection建立了一个对等连接。

点击此处查看GitHub代码

自iOS11之后,WebRTC可以在所有浏览器中工作了,用户可以实时使用。

点击此处运行代码

我嵌入了一个HTML标签,浏览器的安全保护机制不允许这样做。

WebRTC提供了三种API:

从设备中获取音频和视频(mediastream)

建立了一个对等连接(RTCPeerConnection)

传递任意数据(RTCDataChannel)

本文使用了mediastream和PeerConnection.

无服务器的实时对等连接通信客户端

按照如下步骤在同一网页不同客户端之间建立连接

1.实例化两个RTCPeerConnection对象。

2.添加彼此为ICE candidates。

3.对第一个对象使用createoffer建立请求。

4.对两个对象设置本地/远程’描述’。

5.对第二个对象createAnswer。

6.对两个对象设置远程/本地’描述’。

7.进行直接交流。

以下是代码实现

我们从一个react Component出发, 它可以传达两个视频和三个按钮, 并且有一些默认状态可供操作。

class WebRTCPeerConnection extends React.Component { state = { startDisabled: false, callDisabled: true, hangUpDisabled: true, servers: null, pc1: null, pc2: null, localStream: null }; localVideoRef = React.createRef(); remoteVideoRef = React.createRef(); start = () => { // start media devices }; call = () => { // initiate a call }; hangUp = () => { // hang up connection }; render() { const { startDisabled, callDisabled, hangUpDisabled } = this.state; return ( <div> <video ref={this.localVideoRef} autoPlay muted style={{ width: "240px", height: "180px" }} /> <video ref={this.remoteVideoRef} autoPlay style={{ width: "240px", height: "180px" }} /> <div> <button onClick={this.start} disabled={startDisabled}> Start </button> <button onClick={this.call} disabled={callDisabled}> Call </button> <button onClick={this.hangUp} disabled={hangUpDisabled}> Hang Up </button> </div> </div> ); } }

我们使用三个布尔值来控制按钮: null 、 pc1 、 pc2。

第一步:开始

当点击Start开始按钮时,请求视频/音频许可并且开始一个localstream本地流。

start = () => { this.setState({ startDisabled: true }); navigator.mediaDevices .getUserMedia({ audio: true, video: true }) .then(this.gotStream) .catch(e => alert("getUserMedia() error:" + e.name)); }; gotStream = stream => { this.localVideoRef.current.srcObject = stream; this.setState({ callDisabled: false, localStream: stream }); };

使用this.setState来禁止开始按钮,使用navigator.getUserMedia来进入媒体.如果允许,我们在localVideo中开始数据流并且把它加入到状态中。

第二步:调用

现在你可以按Call按钮,这样就启动了两点连接,pc1和pc2,使它们可以相互交流。

1.call开始请求。

2.onCreateOfferSuccess更新pc1,pc2,并且初始化应答。

3.onCreateAnswerSuccess结束连接。

4.gotRemoteStream激发建立第二个视频。

call = () => { this.setState({ callDisabled: true, hangUpDisabled: false }); let { localStream } = this.state; let servers = null, pc1 = new RTCPeerConnection(servers), pc2 = new RTCPeerConnection(servers); pc1.onicecandidate = e => this.onIceCandidate(pc1, e); pc1.oniceconnectionstatechange = e => this.onIceStateChange(pc1, e); pc2.onicecandidate = e => this.onIceCandidate(pc2, e); pc2.oniceconnectionstatechange = e => this.onIceStateChange(pc2, e); pc2.ontrack = this.gotRemoteStream; localStream .getTracks() .forEach(track => pc1.addTrack(track, localStream)); pc1 .createOffer({ offerToReceiveAudio: 1, offerToReceiveVideo: 1 }) .then(this.onCreateOfferSuccess, error => console.error( "Failed to create session description", error.toString() ) ); this.setState({ servers, pc1, pc2, localStream }); };

这段代码几乎是前端模板。

我们开始或禁用相应的按钮,从状态中得到本地流localStream,并且初始化servers, pc1和pc2。

两个pc*都有许多事件听众,onIceCandidate会将其连接,并且通过onIceStateChange打印故障信息,gotRemoteStream会将其添加到正确的video组件。

接着我们把localStream添加到第一个客户端,pc1会产生一个接收视频和音频的请求,当完成这些之后,就更新了组分状态。

onCreateOfferSuccess

当pc1成功请求之后,我们更新客户端中本地和远程的描述。我不确定这些描述包括什么,但是这些资料很重要。

onCreateOfferSuccess = desc => { let { pc1, pc2 } = this.state; pc1 .setLocalDescription(desc) .then( () => console.log("pc1 setLocalDescription complete createOffer"), error => console.error( "pc1 Failed to set session description in createOffer", error.toString() ) ); pc2.setRemoteDescription(desc).then( () => { console.log("pc2 setRemoteDescription complete createOffer"); pc2 .createAnswer() .then(this.onCreateAnswerSuccess, error => console.error( "pc2 Failed to set session description in createAnswer", error.toString() ) ); }, error => console.error( "pc2 Failed to set session description in createOffer", error.toString() ) ); };

pc1更新本地描述,pc2更新远程描述,pc2同样产生了一个回复,就像这样:好,我接受你的请求,我们开始吧。

onCreateAnswerSuccess

当pc2成功恢复后,我们开始建立另一轮描述,只不过这次顺序正好相反。

onCreateAnswerSuccess = desc => { let { pc1, pc2 } = this.state; pc1 .setRemoteDescription(desc) .then( () => console.log( "pc1 setRemoteDescription complete createAnswer" ), error => console.error( "pc1 Failed to set session description in onCreateAnswer", error.toString() ) ); pc2 .setLocalDescription(desc) .then( () => console.log( "pc2 setLocalDescription complete createAnswer" ), error => console.error( "pc2 Failed to set session description in onCreateAnswer", error.toString() ) ); };

pc1建立远程描述而pc2建立本地描述。我想这就像是:从pc1视角看来,对它是本地的,对pc2是远程的,反过来对pc2也一样。

现在,我们就有了两个视频流,可以在同一个网页中相互交流。

onIceCandidate

在这一步,两个pc都说他们得到了ICE candidate。我不知道实际发生了什么,但是这给了我们一个来区分这两个客户端分别在对谁交流的机会。

onIceCandidate = (pc, event) => { let { pc1, pc2 } = this.state; let otherPc = pc === pc1 ? pc2 : pc1; otherPc .addIceCandidate(event.candidate) .then( () => console.log("addIceCandidate success"), error => console.error( "failed to add ICE Candidate", error.toString() ) ); };

我们猜测其它客户端,把它添加成为一个candidate。 如果我们的客户端多于两个,这会很有意思。

第三步:结束

结束很简单,只需要关闭两个客户端。

hangUp = () => { let { pc1, pc2 } = this.state; pc1.close(); pc2.close(); this.setState({ pc1: null, pc2: null, hangUpDisabled: true, callDisabled: false }); };

有趣的地方

第一部分连接,也就是两个客户端互相找到对方,成为发射信号。WebRTC spec没有提到发信号。

发信号在同一个网页两个客户端之间是很简单的,两个客户端就在内存中,只需要启动两个客户端。

但是在现实世界中,你需要这些客户端可以在不同计算机的不同浏览器中运行,如何做才能使它们能互相找到对方呢?如果有上千个客户端呢?

你需要一些通信频道,这些频道知道所有客户端在哪儿,并且说:嗨!你,连接我这里,或者是,你,离开我这里。

这对分布式去中心化区块链不起作用。

相关标签:

发表评论:

评论记录:

未查询到任何数据!