開始使用對等連線

對等連線是 WebRTC 規格中處理連線至不同電腦上的兩個應用程式,以便使用對等通訊協定進行通訊的部分。同儕之間的通訊可以是影片、音訊或任意二進位資料 (適用於支援 RTCDataChannel API 的用戶端)。為了瞭解兩個對等端如何連線,兩個用戶端都需要提供 ICE 伺服器設定。這可能是 STUN 或 TURN 伺服器,其角色是向每個用戶端提供 ICE 候選項目,然後轉移至遠端對等端。這種 ICE 候選值轉移作業通常稱為「訊號傳送」。

訊號

WebRTC 規格包含用於與 ICE (Internet Connectivity Establishment) 伺服器通訊的 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 是 Session Traversal Utilities for NAT 的縮寫,通常會間接用於大多數 WebRTC 應用程式。

TURN (Traversal Using Relay NAT) 是更進階的解決方案,整合了 STUN 通訊協定,且大多數以 WebRTC 為基礎的商業服務都會使用 TURN 伺服器,藉此在對等端之間建立連線。WebRTC API 直接支援 STUN 和 TURN,並在更完整的「Internet 連線建立」一詞下收集。建立 WebRTC 連線時,我們通常會在 RTCPeerConnection 物件的設定中提供一或多個 ICE 伺服器。

Trickle ICE

建立 RTCPeerConnection 物件後,基礎架構會使用提供的 ICE 伺服器,收集連線建立候選項目 (ICE 候選項目)。RTCPeerConnection 上的事件 icegatheringstatechange 會指出 ICE 收集的狀態 (newgatheringcomplete)。

雖然對等端可以等到 ICE 收集作業完成,但通常使用「涓滴式 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 說明文件