WebRTC 連線

本文介紹了各種 WebRTC 相關協議如何相互作用,以建立連線並在對等方之間傳輸資料和/或媒體。

注意: 本頁面需要大量重寫以確保結構完整性和內容完整性。這裡有很多很好的資訊,但由於目前這裡有點像一個垃圾場,所以組織方式一團糟。

信令

不幸的是,WebRTC 無法在沒有中間伺服器的情況下建立連線。我們稱之為 信令通道信令服務。它是在建立連線之前用於交換資訊的任何通訊通道,無論是透過電子郵件、明信片還是信鴿。這由您決定。

我們需要交換的資訊是 Offer(提議)和 Answer(應答),其中只包含下面提到的 SDP

作為連線發起方的對等方 A 將建立一個 Offer。然後,他們將使用所選的信令通道將此 Offer 傳送給對等方 B。對等方 B 將從信令通道接收 Offer 並建立一個 Answer。然後,他們將透過信令通道將其傳送回對等方 A。

會話描述

WebRTC 連線上端點的配置稱為 會話描述。該描述包括有關正在傳送的媒體型別、其格式、正在使用的傳輸協議、端點的 IP 地址和埠以及描述媒體傳輸端點所需的其他資訊。此資訊使用 會話描述協議 (SDP) 進行交換和儲存;如果您想了解 SDP 資料的格式詳細資訊,可以在 RFC 8866 中找到。

當用戶向另一個使用者發起 WebRTC 呼叫時,會建立一個特殊描述,稱為 提議 (offer)。此描述包括有關呼叫方提議的呼叫配置的所有資訊。接收方然後用 應答 (answer) 進行響應,這是對其呼叫端點的描述。透過這種方式,兩臺裝置相互共享交換媒體資料所需的資訊。這種交換使用互動式連線建立 (ICE) 進行處理,該協議允許兩臺裝置使用中介來交換提議和應答,即使兩臺裝置被網路地址轉換 (NAT) 分隔。

因此,每個對等方都保留兩個描述:本地描述 (local description),描述自身;以及 遠端描述 (remote description),描述呼叫的另一端。

提議/應答過程不僅在首次建立呼叫時執行,而且在呼叫的格式或其它配置需要更改的任何時候也執行。無論是新呼叫還是重新配置現有呼叫,以下是交換提議和應答必須發生的基本步驟,暫時不考慮 ICE 層

  1. 呼叫者透過 MediaDevices.getUserMedia 捕獲本地媒體
  2. 呼叫者建立 RTCPeerConnection 並呼叫 RTCPeerConnection.addTrack() (因為 addStream 已棄用)
  3. 呼叫者呼叫 RTCPeerConnection.createOffer() 建立一個提議。
  4. 呼叫者呼叫 RTCPeerConnection.setLocalDescription() 將該提議設定為 本地描述 (即連線本地端的描述)。
  5. 在 setLocalDescription() 之後,呼叫者要求 STUN 伺服器生成 ICE 候選。
  6. 呼叫者使用信令伺服器將提議傳輸給預期的呼叫接收者。
  7. 接收者收到提議並呼叫 RTCPeerConnection.setRemoteDescription() 將其記錄為 遠端描述 (連線另一端的描述)。
  8. 接收者執行其呼叫端所需的任何設定:捕獲其本地媒體,並透過 RTCPeerConnection.addTrack() 將每個媒體軌道附加到對等連線。
  9. 接收者然後透過呼叫 RTCPeerConnection.createAnswer() 建立一個應答。
  10. 接收者呼叫 RTCPeerConnection.setLocalDescription(),傳入建立的應答,以將該應答設定為其本地描述。接收者現在知道連線兩端的配置。
  11. 接收者使用信令伺服器將應答傳送給呼叫者。
  12. 呼叫者收到應答。
  13. 呼叫者呼叫 RTCPeerConnection.setRemoteDescription() 將應答設定為其呼叫端的遠端描述。它現在知道兩個對等方的配置。媒體開始按配置流動。

待處理和當前描述

更深入地瞭解該過程,我們發現 localDescriptionremoteDescription(返回這兩個描述的屬性)並不像它們看起來那麼簡單。因為在重新協商期間,一個提議可能因其提出了不相容的格式而被拒絕,所以每個端點都需要能夠提出新格式,但在被另一個對等方接受之前不能實際切換到該格式。因此,WebRTC 使用 待處理當前 描述。

當前描述(由 RTCPeerConnection.currentLocalDescriptionRTCPeerConnection.currentRemoteDescription 屬性返回)表示連線當前實際使用的描述。這是雙方完全同意使用的最新連線。

待處理描述(由 RTCPeerConnection.pendingLocalDescriptionRTCPeerConnection.pendingRemoteDescription 返回)表示在分別呼叫 setLocalDescription()setRemoteDescription() 後正在考慮的描述。

當讀取描述(由 RTCPeerConnection.localDescriptionRTCPeerConnection.remoteDescription 返回)時,如果存在待處理描述(即,待處理描述不為 null),則返回 pendingLocalDescription/pendingRemoteDescription 的值;否則,返回當前描述(currentLocalDescription/currentRemoteDescription)。

當透過呼叫 setLocalDescription()setRemoteDescription() 更改描述時,指定的描述被設定為待處理描述,WebRTC 層開始評估它是否可接受。一旦提議的描述達成一致,currentLocalDescriptioncurrentRemoteDescription 的值將更改為待處理描述,並且待處理描述再次設定為 null,表示沒有待處理描述。

注意: pendingLocalDescription 不僅包含正在考慮的提議或應答,還包含自提議或應答建立以來已經收集到的任何本地 ICE 候選。同樣,pendingRemoteDescription 包含透過呼叫 RTCPeerConnection.addIceCandidate() 提供的任何遠端 ICE 候選。

有關這些屬性和方法的更多具體資訊,請參閱各個文章;有關 WebRTC 支援的編解碼器以及與哪些瀏覽器相容的資訊,請參閱 WebRTC 使用的編解碼器。編解碼器指南還提供了幫助您選擇最適合您需求的編解碼器的指導。

ICE 候選

除了交換媒體資訊(上面在提議/應答和 SDP 中討論)之外,對等方還必須交換有關網路連線的資訊。這被稱為 ICE 候選,並詳細說明了對等方能夠通訊的可用方法(直接或透過 TURN 伺服器)。通常,每個對等方會首先提出其最佳候選,然後向下推遲到較差的候選。理想情況下,候選是 UDP(因為它更快,並且媒體流能夠相對容易地從中斷中恢復),但 ICE 標準也允許 TCP 候選。

注意: 通常,使用 TCP 的 ICE 候選僅在 UDP 不可用或受限制(使其不適合媒體流)時才會使用。但是,並非所有瀏覽器都支援 ICE over TCP。

ICE 允許候選者表示透過 TCPUDP 的連線,通常更傾向於 UDP(並且得到更廣泛的支援)。每個協議都支援幾種型別的候選者,候選者型別定義了資料如何從對等點傳輸到對等點。

UDP 候選型別

UDP 候選(protocol 設定為 udp 的候選)可以是以下型別之一:

主機

主機候選是指其 ip 地址是遠端對等方的實際直接 IP 地址的候選。

prflx

對等反射候選是指其 IP 地址來自兩個對等方之間的對稱 NAT,通常作為在 trickle ICE(即在主要信令之後但在連線驗證階段完成之前發生的額外候選交換)期間的額外候選。

srflx

伺服器反射候選由 STUN/TURN 伺服器生成;連線發起者向 STUN 伺服器請求一個候選,該伺服器將請求轉發到遠端對等方的 NAT,後者建立並返回一個 IP 地址位於遠端對等方本地的候選。然後,STUN 伺服器以一個 IP 地址與遠端對等方無關的候選回覆發起者的請求。

relay

中繼候選的生成方式與伺服器反射候選 ("srflx") 類似,但使用的是 TURN 而不是 STUN

TCP 候選型別

TCP 候選(即 protocoltcp 的候選)可以是以下型別:

啟用

傳輸將嘗試打開出站連線,但不會接收入站連線請求。這是最常見的型別,也是大多數使用者代理將收集的唯一型別。

passive

傳輸將接收入站連線嘗試,但不會嘗試自身連線。

so

傳輸將嘗試與其對等方同時開啟連線。

選擇候選對

ICE 層選擇兩個對等體之一作為 控制代理。這是 ICE 代理,它將對用於連線的候選對做出最終決定。另一個對等體稱為 受控代理。您可以透過檢查 RTCIceCandidate.transport.role 的值來識別您的連線端是哪個,儘管通常哪個是哪個並不重要。

控制代理不僅負責對使用哪個候選對做出最終決定,而且還負責在必要時使用 STUN 和更新的提議向受控代理傳送該選擇的訊號。受控代理只是等待被告知要使用哪個候選對。

重要的是要記住,一個 ICE 會話可能會導致控制代理選擇多個候選對。每次它這樣做並將該資訊與受控代理共享時,兩個對等方都會重新配置其連線以使用新候選對描述的新配置。

一旦 ICE 會話完成,當前生效的配置就是最終配置,除非發生 ICE 重置。

在每代候選者結束後,會以 RTCIceCandidate 的形式傳送一個候選者結束通知,其 candidate 屬性是一個空字串。此候選者仍應使用 addIceCandidate() 方法照常新增到連線中,以便將該通知傳遞給遠端對等方。

噹噹前協商交換中不再有任何候選者時,會透過傳送一個 RTCIceCandidate 來發送候選者結束通知,其 candidate 屬性為 null。此訊息 不需要 傳送給遠端對等方。它是狀態的遺留通知,可以透過觀察 iceGatheringState 變為 complete,或者透過觀察 icegatheringstatechange 事件來檢測。

當出現問題時

在協商過程中,總會有事情不如意的時候。例如,在重新協商一個已經啟用的連線時——例如,為了適應不斷變化的硬體或網路配置——協商可能會陷入僵局,或者發生某種形式的錯誤,從而完全阻止協商。此外,還可能存在許可權問題或其他問題。

ICE 回滾

當重新協商一個已經啟用的連線,並且出現協商失敗的情況時,您並不希望終止正在執行的通話。畢竟,您很可能只是試圖升級或降級連線,或者對正在進行的會話進行其他調整。在這種情況下,終止通話將是過度的反應。

相反,您可以啟動 ICE 回滾。回滾將 SDP 提議(以及由此推斷的連線配置)恢復到連線的 signalingStatestable 時的配置。

要以程式設計方式發起回滾,請傳送一個 typerollback 的描述。描述物件中的任何其他屬性都將被忽略。

此外,當曾經建立過提議的對等方收到遠端對等方的提議時,ICE 代理將自動發起回滾。換句話說,如果本地對等方處於 have-local-offer 狀態,表示本地對等方之前 傳送 了一個提議,那麼使用 收到的 提議呼叫 setRemoteDescription() 將觸發回滾,以便協商從遠端對等方作為呼叫方切換到本地對等方作為呼叫方。

ICE 重啟

瞭解 ICE 重啟 過程。

複雜圖表中的整個交換過程

A complete architectural diagram showing the whole WebRTC process.

原始來源