使用 WebRTC 数据通道

在本指南中,我们将探讨如何向对等连接添加数据通道,然后可以使用该通道安全地交换任意数据;也就是说,我们可以选择任何类型的数据,以及任何格式。

注意:由于所有 WebRTC 组件都需要使用加密,因此通过RTCDataChannel传输的任何数据都会自动使用数据报传输层安全 (DTLS) 进行保护。有关更多信息,请参阅下面的安全

创建数据通道

RTCDataChannel使用的底层数据传输可以通过两种方式之一创建

  • 让 WebRTC 创建传输并将其告知远程对等方(通过使其接收datachannel事件)。这是简单的方法,适用于各种用例,但可能不够灵活以满足您的需求。
  • 编写自己的代码来协商数据传输,并编写自己的代码来向另一个对等方发出信号,指示它需要连接到新通道。

让我们看看这两种情况,首先从最常见的第一种情况开始。

自动协商

通常,您可以允许对等连接为您处理RTCDataChannel连接的协商。为此,请调用createDataChannel(),而不为negotiated属性指定值,或使用值为false的属性指定。这将自动触发RTCPeerConnection为您处理协商,导致远程对等方创建数据通道并将这两个通道链接到网络中。

RTCDataChannel对象由createDataChannel()立即返回;您可以通过监视发送到RTCDataChannelopen事件来判断连接是否已成功建立。

js
let dataChannel = pc.createDataChannel("MyApp Channel");

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});

手动协商

要手动协商数据通道连接,您需要首先使用RTCPeerConnection上的createDataChannel()方法创建一个新的RTCDataChannel对象,并在选项中指定设置为truenegotiated属性。这表示对等连接不应代表您尝试协商通道。

然后使用 Web 服务器或其他方法带外协商连接。此过程应向远程对等方发出信号,指示它应使用相同的id创建自己的RTCDataChannel,并将negotiated属性也设置为true。这将通过RTCPeerConnection链接这两个对象。

js
let dataChannel = pc.createDataChannel("MyApp Channel", {
  negotiated: true,
});

dataChannel.addEventListener("open", (event) => {
  beginTransmission(dataChannel);
});

requestRemoteChannel(dataChannel.id);

在此代码片段中,通道以negotiated设置为true创建,然后使用名为requestRemoteChannel()的函数触发协商,以创建与本地通道具有相同 ID 的远程通道。

这样做可以让您使用不同的属性为每个对等方创建数据通道,并通过对id使用相同的值来声明式地创建通道。

缓冲

WebRTC 数据通道支持出站数据的缓冲。这是自动处理的。虽然无法控制缓冲区的大小,但您可以了解当前缓冲了多少数据,并且可以选择在缓冲区开始内存不足时通过事件获得通知。这使得编写高效的例程变得容易,这些例程可以确保始终有数据可以发送,而不会过度使用内存或完全淹没通道。

了解消息大小限制

对于通过网络传输的任何数据,都存在大小限制。从根本上讲,各个网络数据包不能大于某个值(确切的数字取决于网络和使用的传输层)。在应用程序级别(即,在您的代码正在运行的用户代理的 WebRTC 实现中),WebRTC 实现实现了支持大于网络传输层最大数据包大小的消息的功能。

这可能会使事情变得复杂,因为您不一定知道各种用户代理的大小限制是什么,以及它们在发送或接收较大消息时的响应方式。即使用户代理共享相同的用于处理流控制传输协议 (SCTP) 数据的基础库,由于库的使用方式不同,仍然可能存在差异。例如,Firefox 和 Google Chrome 都使用usrsctp库来实现 SCTP,但仍然存在由于它们调用库和对库返回的错误的反应方式不同而导致RTCDataChannel上的数据传输失败的情况。

当两个运行 Firefox 的用户在数据通道上通信时,消息大小限制远大于 Firefox 和 Chrome 通信时,因为 Firefox 实现了一种现在已弃用的技术,用于在多个 SCTP 消息中发送大型消息,而 Chrome 则没有。Chrome 反而会看到一系列它认为已完成的消息,并将它们作为多条消息传递到接收到的RTCDataChannel

小于 16 KiB 的消息可以毫无顾虑地发送,因为所有主要用户代理都以相同的方式处理它们。除此之外,事情变得更加复杂。

大型消息的问题

目前,使用RTCDataChannel发送大于 64 KiB 的消息(如果要支持跨浏览器的数据交换,则为 16 KiB)并不实用。问题在于 SCTP(用于在RTCDataChannel上发送和接收数据的协议)最初设计为用于信令协议。预计消息将相对较小。几乎是作为事后想法添加了对大于网络层MTU的消息的支持,以防信令消息需要大于 MTU。此功能要求消息的每一部分都具有连续的序列号,因此必须一个接一个地传输它们,而没有任何其他数据交织在它们之间。

这最终成为一个问题。随着时间的推移,各种应用程序(包括那些实现 WebRTC 的应用程序)开始使用 SCTP 传输越来越大的消息。最终意识到,当消息变得太大时,大型消息的传输可能会阻止该数据通道上的所有其他数据传输——包括关键的信令消息。

当浏览器正确支持当前用于支持更大消息的标准(即指示消息是应视为单个有效负载的一系列消息中的最后一个消息的记录结束 (EOR) 标志)时,这将成为一个问题。Firefox 57 中已实现此功能,但 Chrome 中尚未实现(请参阅Chromium 错误 7774)。在 EOR 支持到位后,RTCDataChannel 有效负载可以大得多(官方最多可达 256 KiB,但 Firefox 的实现将其限制在高达 1 GiB)。即使是 256 KiB,也足以导致处理紧急流量时出现明显的延迟。如果数据量更大,除非您确定操作条件,否则延迟可能会变得无法接受。

为了解决此问题,已经设计了一种新的流调度器系统(通常称为“SCTP ndata 规范”),以使能够交错发送在不同流上发送的消息,包括用于实现 WebRTC 数据通道的流。此提案仍处于 IETF 草案形式,但一旦实施,它将使能够发送基本上没有大小限制的消息,因为 SCTP 层将自动交错底层子消息以确保每个通道的数据都有机会通过。

Firefox 对 ndata 的支持正在实施中;请参阅Firefox 错误 1381145以跟踪它何时可供一般使用。Chrome 团队正在Chrome 错误 5696中跟踪其 ndata 支持的实现。

注意:本节中的许多信息部分基于 Lennart Grahl 编写的博客文章深入了解 WebRTC 数据通道消息大小限制。他在那里更详细地介绍了这一点,但由于浏览器自那时以来已更新,因此其中一些内容可能已过时。此外,随着时间的推移,它将变得更加如此,尤其是在主要浏览器中完全集成 EOR 和 ndata 支持后。

安全

使用 WebRTC 传输的所有数据都已加密。对于RTCDataChannel,使用的加密是数据报传输层安全 (DTLS),它基于传输层安全 (TLS)。由于 TLS 用于保护每个 HTTPS 连接,因此您在数据通道上发送的任何数据都与用户浏览器发送或接收的任何其他数据一样安全。

更根本地说,由于 WebRTC 是两个用户代理之间的点对点连接,因此数据永远不会通过 Web 或应用程序服务器。这减少了数据被拦截的机会。