点对点连接是 WebRTC 规范的一部分,该规范旨在对点一台计算机上的两台应用进行连接,以使用点对点协议进行通信。对等设备之间的通信可以是视频、音频或任意二进制数据(适用于支持 RTCDataChannel
API 的客户端)。为了发现两个对等端如何连接,两个客户端都需要提供 ICE Server 配置。这是 STUN 或 TURN 服务器,其作用是向每个客户端提供 ICE 候选对象,然后这些客户端将被传输到远程对等方。这种转移 ICE 候选对象的方式通常称为信号。
信令
WebRTC 规范包含用于与 ICE(互联网连接建立)服务器通信的 API,但信令组件并不属于该组件。需要发出信号才能让两个对等网络共享它们之间的连接方式。这通常可以通过基于 HTTP 的常规 Web API(即 REST 服务或其他 RPC 机制)解决,在此过程中,网络应用可在发起对等连接之前中继必要的信息。
以下代码段展示了如何使用虚构信号服务异步发送和接收消息。必要时,本指南的其余示例将使用该方法。
// Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
// New message from remote client received
});
// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');
信令可以通过许多不同的方式实现,WebRTC 规范不偏好任何特定的解决方案。
启动对等连接
每个对等连接都由一个 RTCPeerConnection
对象处理。此类的构造函数接受单个 RTCConfiguration
对象作为其参数。此对象定义对等连接的设置方式,应包含关于要使用的 ICE 服务器的信息。
创建 RTCPeerConnection
后,我们需要创建 SDP 优惠或答案,具体取决于我们是发起通话的对等方还是接收方的对等方。SDP 优惠或应答一经创建,就必须通过其他信道发送给远程对等方。将 SDP 对象传递给远程对等设备的过程称为信号,不在 WebRTC 规范的涵盖范围内。
为了从调用方启动对等连接设置,我们创建一个 RTCPeerConnection
对象,然后调用 createOffer()
以创建 RTCSessionDescription
对象。此会话说明设置为使用 setLocalDescription()
的本地说明,然后通过我们的信令通道发送到接收端。我们还针对接收方收到所提供会话说明的回答时,针对信令渠道设置了监听器。
async function makeCall() {
const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
if (message.answer) {
const remoteDesc = new RTCSessionDescription(message.answer);
await peerConnection.setRemoteDescription(remoteDesc);
}
});
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
signalingChannel.send({'offer': offer});
}
在接收端,我们会等待传入的优惠,然后再创建 RTCPeerConnection
实例。完成后,我们使用 setRemoteDescription()
设置收到的优惠。接下来,我们调用 createAnswer()
为收到的优惠创建答案。系统会使用 setLocalDescription()
将此答案设置为本地说明,然后通过我们的信令服务器将其发送至发起调用的一方。
const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
if (message.offer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
signalingChannel.send({'answer': answer});
}
});
两个对等方同时设置了本地和远程会话说明之后,他们就会了解远程对等方的功能。这并不意味着对等设备之间的连接已准备就绪。为此,我们需要在每个对等端收集 ICE 候选项,并通过信令通道传输给另一个对等方。
ICE 候选字词
两个对等方必须用 WebRTC 交换连接信息,然后才能使用 WebRTC 进行通信。由于网络条件可能因多种因素而发生变化,因此通常使用外部服务来发现连接到对等网络的潜在候选对象。此服务称为 ICE,使用的是 STUN 或 TURN 服务器。STUN 代表用于 NAT 的会话遍历实用程序,通常在大多数 WebRTC 应用中间接使用。
TURN(Traversal using Relay NAT)是一种整合了 STUN 协议的更高级解决方案,大多数基于 WebRTC 的商业服务都使用 TURN 服务器在对等方之间建立连接。WebRTC API 直接支持 STUN 和 TURN,在更完整的术语“互联网连接建立”下收集。创建 WebRTC 连接时,我们通常会在 RTCPeerConnection
对象的配置中提供一个或多个 ICE 服务器。
Trickle ICE
创建 RTCPeerConnection
对象后,底层框架会使用提供的 ICE 服务器收集连接建立的候选对象(ICE 候选对象)。RTCPeerConnection
上的事件 icegatheringstatechange
会指示 ICE 收集的状态为(new
、gathering
或 complete
)。
虽然对等设备可以等待 ICE 收集完成,但通常要高效地使用“滚动冰”技术,并在发现每个 ICE 候选设备后将其传输到远程对等设备。这将大大缩短对等连接的设置时间,并允许视频通话以更低的延迟开始。
要收集 ICE 候选对象,只需为 icecandidate
事件添加监听器即可。针对该监听器发出的 RTCPeerConnectionIceEvent
将包含 candidate
属性,该属性表示应发送到远程对等端的新候选音频(请参阅信号)。
// Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
if (event.candidate) {
signalingChannel.send({'new-ice-candidate': event.candidate});
}
});
// Listen for remote ICE candidates and add them to the local RTCPeerConnection
signalingChannel.addEventListener('message', async message => {
if (message.iceCandidate) {
try {
await peerConnection.addIceCandidate(message.iceCandidate);
} catch (e) {
console.error('Error adding received ice candidate', e);
}
}
});
已建立连接
收到 ICE 候选对象后,我们的对等连接状态最终会变为已连接状态。为了检测这一点,我们在 RTCPeerConnection
中添加一个监听器,用于监听 connectionstatechange
事件。
// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// Peers connected!
}
});