開始使用對等連線

對等互連是 WebRTC 規格的一部分,負責連線不同電腦上的兩個應用程式,以便使用對等互連通訊協定進行通訊。同層級之間的通訊可以是視訊、音訊或任意二進位資料 (適用於支援 RTCDataChannel API 的用戶端)。如要瞭解兩個對等互連裝置如何連線,兩個用戶端都必須提供 ICE 伺服器設定。這是 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 通訊。由於網路狀況會因多種因素而異,因此通常會使用外部服務,找出可連線至對等互連裝置的候選項目。這項服務稱為 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 收集作業的狀態 (newgatheringcomplete)。

雖然對等互連裝置可以等到 ICE 收集完成再傳送候選項目,但通常使用「trickle 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!
    }
});

RTCPeerConnection API 說明文件