WebRTC 连接
本文介绍了各种 WebRTC 相关协议如何相互交互以建立连接并在对等方之间传输数据和/或媒体。
注意: 此页面需要大量重写以确保结构完整性和内容完整性。这里有很多信息很好,但组织混乱,因为现在这有点像垃圾箱。
信令
不幸的是,WebRTC 无法在没有中间服务器的情况下创建连接。我们称之为信令通道或信令服务。它是任何类型的通信通道,用于在建立连接之前交换信息,无论是通过电子邮件、明信片还是信鸽。这取决于你。
我们需要交换的信息是 Offer 和 Answer,它们只包含下面提到的 SDP。
对等方 A 将是连接的启动者,它将创建一个 Offer。然后,他们将使用所选的信令通道将此 Offer 发送到对等方 B。对等方 B 将从信令通道接收 Offer 并创建一个 Answer。然后,他们将此 Answer 发送回对等方 A,沿着信令通道。
会话描述
WebRTC 连接中端点的配置称为会话描述。描述包括有关正在发送的媒体类型、格式、正在使用的传输协议、端点的 IP 地址和端口以及描述媒体传输端点所需的其他信息。此信息使用会话描述协议 (SDP) 进行交换和存储;如果你想了解 SDP 数据格式的详细信息,可以在 RFC 8866 中找到。
当用户开始向另一个用户进行 WebRTC 通话时,会创建一个名为offer的特殊描述。此描述包含有关呼叫方为呼叫提出的所有配置信息。然后,接收者以answer形式进行响应,其中包含其通话端点的描述。这样,两台设备之间就可以共享进行媒体数据交换所需的信息。这种交换使用交互式连接建立 (ICE) 进行处理,该协议允许两台设备使用中介交换 offer 和 answer,即使这两台设备被网络地址转换 (NAT) 隔开。
因此,每个对等方都保留着两个描述:本地描述,描述自身,以及远程描述,描述通话的另一端。
offer/answer 过程在首次建立通话时执行,但在通话格式或其他配置需要更改时也会执行。无论是否为新通话,还是重新配置现有通话,这些都是必须执行的基本步骤,以便交换 offer 和 answer,暂时不考虑 ICE 层。
- 呼叫方通过
MediaDevices.getUserMedia
捕获本地媒体 - 呼叫方创建
RTCPeerConnection
并调用RTCPeerConnection.addTrack()
(因为addStream
正在弃用) - 呼叫方调用
RTCPeerConnection.createOffer()
来创建 offer。 - 呼叫方调用
RTCPeerConnection.setLocalDescription()
来将 offer 设置为本地描述(即连接本地端点的描述)。 - 在 setLocalDescription() 之后,呼叫方请求 STUN 服务器生成 ICE 候选。
- 呼叫方使用信令服务器将 offer 传输到预期的呼叫接收方。
- 接收方接收 offer 并调用
RTCPeerConnection.setRemoteDescription()
来将其记录为远程描述(连接另一端点的描述)。 - 接收方执行其通话端点所需的任何设置:捕获其本地媒体,并将每个媒体轨道附加到对等连接中,方法是通过
RTCPeerConnection.addTrack()
- 然后,接收方通过调用
RTCPeerConnection.createAnswer()
来创建 answer。 - 接收方调用
RTCPeerConnection.setLocalDescription()
,传入创建的 answer,将 answer 设置为其本地描述。现在,接收方知道连接两端点的配置。 - 接收方使用信令服务器将 answer 发送到呼叫方。
- 呼叫方接收 answer。
- 呼叫方调用
RTCPeerConnection.setRemoteDescription()
来将 answer 设置为其通话端点的远程描述。现在它知道两个对等方的配置。媒体开始按配置流动。
待处理和当前描述
深入了解这个过程,我们会发现 localDescription
和 remoteDescription
这些返回这两个描述的属性并没有看起来那么简单。因为在重新协商期间,offer 可能会由于它提出的格式不兼容而被拒绝,因此每个端点都必须能够提出新的格式,但只有在被另一个对等方接受后才能真正切换到该格式。出于这个原因,WebRTC 使用待处理和当前描述。
当前描述(由 RTCPeerConnection.currentLocalDescription
和 RTCPeerConnection.currentRemoteDescription
属性返回)表示连接当前实际使用的描述。这是双方完全同意使用的最新连接。
待处理描述(由 RTCPeerConnection.pendingLocalDescription
和 RTCPeerConnection.pendingRemoteDescription
返回)表示在分别调用 setLocalDescription()
或 setRemoteDescription()
后,当前正在考虑的描述。
读取描述(由 RTCPeerConnection.localDescription
和 RTCPeerConnection.remoteDescription
返回)时,如果存在待处理描述(即,待处理描述不是 null
),则返回的值为 pendingLocalDescription
/pendingRemoteDescription
的值;否则,返回当前描述(currentLocalDescription
/currentRemoteDescription
)。
通过调用 setLocalDescription()
或 setRemoteDescription()
来更改描述时,指定的描述将设置为待处理描述,并且 WebRTC 层开始评估它是否可接受。一旦提出的描述得到双方同意,currentLocalDescription
或 currentRemoteDescription
的值将更改为待处理描述,并且待处理描述将再次设置为 null,表示不再存在待处理描述。
注意: pendingLocalDescription
不仅包含正在考虑的 offer 或 answer,还包含自创建 offer 或 answer 以来已收集的任何本地 ICE 候选。同样,pendingRemoteDescription
包含通过调用 RTCPeerConnection.addIceCandidate()
提供的任何远程 ICE 候选。
有关这些属性和方法的详细信息,请参阅各自的文章,并参阅 WebRTC 使用的编解码器,了解 WebRTC 支持的编解码器以及哪些编解码器与哪些浏览器兼容。编解码器指南还提供指导,帮助您选择最适合您需求的编解码器。
ICE 候选
除了交换有关媒体的信息(如上面“Offer/Answer”和 SDP 中所述)外,对等方还必须交换有关网络连接的信息。这被称为 **ICE 候选**,它详细说明了对等方能够通信的可用方法(直接或通过 TURN 服务器)。通常,每个对等方会首先提出其最佳候选,然后依次向下,直到到达最差的候选。理想情况下,候选是 UDP(因为它更快,并且媒体流能够从中断中相对容易地恢复),但 ICE 标准也允许 TCP 候选。
**注意:** 通常,使用 TCP 的 ICE 候选仅在 UDP 不可用或受限到使其不适合媒体流的方式时才会使用。但是,并非所有浏览器都支持通过 TCP 的 ICE。
ICE 允许候选代表通过 TCP 或 UDP 的连接,通常更倾向于 UDP(并且更广泛地支持)。每个协议都支持几种类型的候选,候选类型定义了数据从对等方到对等方的传输方式。
UDP 候选类型
UDP 候选(其 protocol
设置为 udp
的候选)可以是以下类型之一
TCP 候选类型
选择候选对
ICE 层选择两个对等方中的一个作为 **控制代理**。这是 ICE 代理,它将最终决定使用哪个候选对进行连接。另一个对等方称为 **受控代理**。您可以通过检查 RTCIceCandidate.transport.role
的值来确定您的连接端是哪个,尽管通常情况下哪个是哪个并不重要。
控制代理不仅负责做出最终决定使用哪个候选对,还负责通过使用 STUN 和更新的提议(如果需要)向受控代理发出信号。受控代理只等待指示使用哪个候选对。
重要的是要注意,单个 ICE 会话可能会导致控制代理选择多个候选对。每次这样做并与受控代理共享该信息时,两个对等方都会重新配置其连接以使用新候选对描述的新配置。
ICE 会话完成后,当前生效的配置是最终配置,除非发生 ICE 重置。
在每次生成候选结束时,都会以 RTCIceCandidate
的形式发送一个候选结束通知,该通知的 candidate
属性为空字符串。此候选仍然应该像往常一样使用 addIceCandidate()
方法添加到连接中,以便将该通知传递给远程对等方。
当在当前协商交换过程中不再期望有任何更多候选时,会通过传递一个 RTCIceCandidate
来发送候选结束通知,该通知的 candidate
属性为 null
。此消息 *不需要* 发送到远程对等方。这是一个状态的传统通知,可以通过监视 iceGatheringState
更改为 complete
以及监视 icegatheringstatechange
事件来检测。
出现问题时
在协商期间,有时事情会无法正常进行。例如,当重新协商连接时(例如,为了适应不断变化的硬件或网络配置),协商可能会陷入僵局,或者可能会发生某种形式的错误,完全阻止协商。同样,也可能存在权限问题或其他问题。
ICE 回滚
当重新协商已经处于活动状态的连接并且出现协商失败的情况时,您实际上并不想终止正在运行的通话。毕竟,您很可能只是试图升级或降级连接,或者对正在进行的会话进行其他调整。在这种情况下,中止通话将是过度的反应。
相反,您可以启动 **ICE 回滚**。回滚将 SDP 提议(以及扩展的连接配置)恢复到连接的 signalingState
最后一次为 stable
时的配置。
要以编程方式启动回滚,请发送一个其 type
为 rollback
的描述。描述对象中的任何其他属性都将被忽略。
此外,当先前创建提议的对等方从远程对等方接收到提议时,ICE 代理将自动启动回滚。换句话说,如果本地对等方处于 have-local-offer
状态,表明本地对等方先前已 *发送* 了提议,那么调用 setRemoteDescription()
使用 *接收* 的提议将触发回滚,以便协商从远程对等方作为呼叫方切换到本地对等方作为呼叫方。
ICE 重启
了解 ICE 重启 流程。