ピア接続は、異なるコンピュータ上の 2 つのアプリケーションを接続してピアツーピア プロトコルを使用して通信する WebRTC 仕様の一部です。ピア間の通信は、動画、音声、任意のバイナリ データ(RTCDataChannel
API をサポートするクライアントの場合)です。2 つのピアを接続する方法を見つけるには、両方のクライアントが ICE サーバー構成を提供する必要があります。これは STUN または TURN サーバーのいずれかであり、各クライアントに ICE 候補を提供し、リモートピアに転送します。この ICE 候補の転送は、一般にシグナリングと呼ばれます。
シグナリング
WebRTC 仕様には ICE(インターネット接続確立)サーバーとの通信用の API が含まれていますが、シグナリング コンポーネントは含まれていません。2 つのピアが接続方法を共有するには、シグナリングが必要です。通常、これは通常の 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});
}
});
2 つのピアがローカル セッションとリモート セッションの両方の説明を設定すると、リモート ピアの機能を把握できます。これは、ピア間の接続の準備が整っているという意味ではありません。これが機能するには、各ピアで ICE 候補を収集し、(シグナリング チャネル経由で)他のピアに転送する必要があります。
ICE の候補者
2 つのピアが WebRTC を使用して通信するには、接続情報を交換する必要があります。ネットワークの状態はさまざまな要因によって異なる可能性があるため、通常、ピアに接続する候補を検出するために外部サービスが使用されます。このサービスは ICE と呼ばれ、STUN または TURN サーバーのいずれかを使用します。STUN は NAT のセッション トラバーサル ユーティリティの略で、通常はほとんどの WebRTC アプリケーションで間接的に使用されます。
TURN(リレー NAT を使用した走査)は、STUN プロトコルを組み込んだ高度なソリューションです。ほとんどの商用 WebRTC ベースのサービスは、TURN サーバーを介してピア間の接続を確立します。WebRTC API は STUN と TURN の両方を直接サポートしており、より包括的な用語であるインターネット接続の確立にまとめられています。WebRTC 接続を作成する場合は、通常、RTCPeerConnection
オブジェクトの構成で 1 つ以上の ICE サーバーを指定します。
トリクル ICE
RTCPeerConnection
オブジェクトが作成されると、基盤となるフレームワークは、指定された ICE サーバーを使用して接続確立の候補(ICE 候補)を収集します。RTCPeerConnection
のイベント icegatheringstatechange
は、ICE 収集の状態(new
、gathering
、complete
)を示します。
ピアは 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 候補が受信されると、ピア接続の状態は最終的に接続状態に変わるはずです。これを検出するには、connectionstatechange
イベントをリッスンするリスナーを RTCPeerConnection
に追加します。
// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// Peers connected!
}
});