開始使用對等連線

對等連線是 WebRTC 規格的一部分,它可用來處理不同電腦上的兩個應用程式,透過點對點通訊協定通訊。對等點之間的通訊可以是視訊、音訊或任意二進位資料 (適用於支援 RTCDataChannel API 的用戶端)。為了瞭解兩個對等點如何連線,兩個用戶端需要提供 ICE 伺服器設定。這是一種 STUN 或 TURN 伺服器,其角色是為每個用戶端提供 ICE 候選對象,然後再將他們傳輸至遠端對等互連。這類 ICE 候選人的轉移通常稱為信號。

訊號

WebRTC 規格包含用來與 ICE (網際網路連線能力) 伺服器通訊的 API,但信號元件不屬於其中的一部分。需要簽名才能使用兩個對等的節點共用這些連線方式。這通常是透過一般以 HTTP 為基礎的網路 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 (使用轉發 NAT 時)「進階」解決方案整合了 STUN 通訊協定,而大部分商用 WebRTC 服務都使用 TURN 伺服器在對等點之間建立連線。WebRTC API 直接支援 STUN 和 TURN,而且在完全完整的網際網路連線能力下,都會收集 STUN。建立 WebRTC 連線時,我們通常會在 RTCPeerConnection 物件設定中提供一或多個 ICE 伺服器。

特里克 (ICE)

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

雖然同仁可以等待 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 說明文件