WebRTC对等连接(二)- 连接不同设备上的浏览器

原文标题:Learning WebRTC peer-to-peer communication, part 2
作者:Swizec Teller

WebRTC对等通信-在不同设备上连接浏览器

我们成功了!不需要服务器即可使不同设备上的两个浏览器相互交流,只需要在开始交互的时候使用服务器。
点击此处运行代码,在不同的设备上打开两个链接。

如何使用WebRTC连接不同设备上的浏览器

111

你可以使用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可以与服务器交流。实例化类,建立连接,收听信息。

222

我们在connetToSocket中建立了一个new WebSocket,加入一些反馈,期待好的效果。Onmessage允许我们之后通过messageListeners数组添加额外的message listeners 窗口。

SendToServer 允许向服务器发送JSON对象,并且addMsgListener允许添加一个新的message listener 窗口。 我们使用它来连接PeerConnection 和服务器。

建立PeerConnection接口

WebRTC part1 可以学到, 我们把RTCPeerConnection组件分成了help class

以下148行代码完成了整个周期。我们之前讨论过此代码,这是重述。

333

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类

HangUpcloseVideoCall共同工作来停止调用。我们需要两者因为其中一个是用户控制的,而当挂断命令从另一边传来的时候,另一个被调用。

最后一步

在粘合区有一条信令服务器发出的信息需要我们处理: 调用请求。

    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);
    }

成功工作

将所有部分结合,现在你可以在不同设备的两个浏览器上相互交流而不再需要服务器。

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营