Signaling Server — WebRTCインフラのサーバーコンポーネントで、ピア間のメタデータ交換を行い、接続の確立と終了を可能にします。メディアトラフィックとは異なり、シグナリングはWebSocket、HTTP、XMPP、SIPなど、いかなるプロトコルでも送信できます。MDN Web Docs、2024によれば、シグナリングはいかなるWebRTCアプリケーションにおいても必須コンポーネントであり、プロトコルがシグナリングメッセージのやり方を特定しないためです。
ポイント
Signaling Server — 2つ以上のピア間でWebRTC接続を確立するプロセスを協調するネットワークサービスです。メディアデータ(音声、動画、DataChannelデータ)を伝送せず、ピア発見と接続パラメータ交渉に必要な制御情報のみを伝送します。P2Pチャネルが正常に構築された後は、Signaling Serverは不要になる場合がありますが、いくつかのアーキテクチャでは後続のシグナル交換(コールの終了、参加者の追加)のために残ります。
シグナリングアーキテクチャは3つのコンポーネントで構成されます: Signaling Server、シグナルチャネル(クライアントとサーバー間のトランスポートプロトコル)、およびクライアントAPI(通常ブラウザのWebRTCスタックに組み込まれています)。WebRTC仕様(W3C、2024)は意図的にシグナリングプロトコルを標準化しておらず、開発者はアプリケーションに適したトランスポートを選べます。この柔軟なアプローチにより、WebアプリにWebSocket、チャットシステムにXMPP、電話インフラとの統合にSIPを使用できます。
WebRTC接続を確立する前に、ピアは3タイプのメッセージを交換する必要があります: セッション説明書(offerとanswer)、ICE候補、およびセッションの終了/変更情報。Signaling Serverは、ルームまたはユーザーIDを使用してピア間でこれらのメッセージをルーティングします。標準的なパターンは「ルーム」を作成し、2人の参加者が接続し、サーバーが各参加者から相手だけにメッセージを中継するものです。
Signaling Serverは、次のような通常のWebRTC接続確立プロトコルを実装します。ピアはWebSocket(または他のトランスポート)を経由してサーバーに接続し、ルームにレジスタします。ピアA(発起者)はRTCPeerConnection.createOffer()を使用してoffer(出力メディアストリームのSDP説明書)を作成し、それをローカル説明書として設定し、Signaling Serverに送信します。サーバーはofferをピアBに中継します。ピアBはofferを受け取り、それをリモート説明書として設定し、createAnswer()でanswerを作成し、それをローカル説明書として設定し、サーバーを経由して送り戻します。このプロセスをSDP Offer/Answerといいます。
SDP交換と並行して、各ピアはICE候補(host、srflx、relay)を収集し、Signaling Serverを経由して相手のピアに送信します。リモートピアは受信した候補をRTCPeerConnection.addIceCandidate()で追加します。ICEプロセスは、効くパスを見つけるために候補のすべての組み合わせをテストします。効くパスが見つかった後(通常1–5秒)、メディアトラフィックはピア間で直接流れ始め、Signaling Serverはデータ伝送に参加しなくなります。次の制御イベント(コールの終了、ストリーム品質の変更)まで、その役割は終了します。
メッセージのアドレッシングには、Signaling Serverはルームまたはチャネルメカニズムを使用します。新しいWebRTCセッションはそのごとに、固有ID(通常UUID)を持つルームが作成されます。発起者がルームを作成し、第2のピアが接続するのを待ちます。第2のピアは、外部チャネル(例:招待リンク)を通じて受け取ったIDを使用してルームに参加します。サーバーはルームのマップを管理し、各IDに接続クライアントのリストが対応します。参加者の数が2人に達すると、サーバーはピア間でシグナリングメッセージの中継を開始します。
Signaling Serverは、それぞれに利点と缺点がある異なるトランスポートプロトコルを使用できます。プロトコルの選択は、アプリケーションの種類、インフラの制約、互換性の要件に依存します。以下は、最も一般的なプロトコルとその特徴です。
| プロトコル | トランスポート | 利点 | 缺点 |
|---|---|---|---|
| WebSocket | TCP | 全双方向、低遅延、ブラウザに組み込み | スケーリングの複雑さ、プロキシのブロック |
| HTTP/SSE | TCP | 様々なインフラと互換性が高い、実装が簡単 | 単方向のみ(サーバー→クライアント)、ポーリングが必要 |
| XMPP | TCP | 標準化されている、認証サポート、拡張可能 | 簡単なシナリオには過剰、XMLオーバーヘッド |
| SIP | UDP/TCP | VoIPや電話インフラとの統合 | 複雑、ブラウザにネイティブでない |
| MQTT | TCP | 軽量、IoT環境で動作、publish/subscribe | ブローカーが必要、追加の遅延 |
WebSocketは、WebアプリにおけるSignaling Serverの最も人気のあるプロトコルです。SDPやICE候補の非同期交換に重要な全双方向通信を提供し、すべての現代的なブラウザがWebSocket APIを通じてネイティブにサポートしています。サーバーサイドのWebSocket実装は、すべての人気プラットフォーム(Node.js、Python、Java、Go)で利用可能です。数百万ユーザーを持つアプリでは、Signaling Serverインスタンス間の同期にRedis Pub/SubやKafkaをベースとしたスケーラブルなWebSocketソリューションが使用されています。
wsライブラリ(WebSocket)と組み込みHTTPサーバーを使用したNode.jsでの簡単なSignaling Server実装を見てみましょう。サーバーは、ユーザーレジストレーション、ルーム作成、参加者間のメッセージ中継をサポートします。
const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 8080 });
const rooms = new Map();
server.on("connection", (ws) => {
ws.roomId = null;
ws.on("message", (data) => {
const msg = JSON.parse(data);
switch (msg.type) {
case "join":
handleJoin(ws, msg.roomId);
break;
case "offer":
case "answer":
case "ice-candidate":
relayToPeer(ws, msg);
break;
case "leave":
handleLeave(ws);
break;
}
});
ws.on("close", () => handleLeave(ws));
});
function handleJoin(ws, roomId) {
if (!rooms.has(roomId)) {
rooms.set(roomId, []);
}
const room = rooms.get(roomId);
room.push(ws);
ws.roomId = roomId;
if (room.length === 2) {
room[0].send(JSON.stringify({ type: "peer-joined" }));
room[1].send(JSON.stringify({ type: "peer-joined" }));
}
}
function relayToPeer(sender, msg) {
const room = rooms.get(sender.roomId);
if (!room) return;
room.forEach(peer => {
if (peer !== sender && peer.readyState === WebSocket.OPEN) {
peer.send(JSON.stringify(msg));
}
});
}
function handleLeave(ws) {
if (!ws.roomId) return;
const room = rooms.get(ws.roomId);
if (!room) return;
const idx = room.indexOf(ws);
if (idx !== -1) room.splice(idx, 1);
if (room.length === 0) rooms.delete(ws.roomId);
}
このSignaling Serverは、ルームへの接続、2つのピア間のWebRTCメッセージ(offer、answer、ice-candidate)の中継、および接続切断管理といった基本機能を実装しています。サーバーは、接続しているWebSocketクライアントをもつルームを保管するためにMapを使用します。relayToPeer関数は、送信元を除いたルーム内のすべての参加者にメッセージを送信します。プロダクションでは、メッセージタイプの検証、JSON解析エラーの処理、切断接続を検知するためのheartbeatメカニズムを追加する必要があります。
クライアント側では、Signaling ServerはブラウザのWebSocket APIを通じて統合されます。クライアントはサーバーへの接続を確立し、ルームへの参加をリクエストし、受信したWebRTCメッセージをsetRemoteDescription()やaddIceCandidate()を通じてRTCPeerConnectionに渡して処理します。クライアントコードは、onicecandidateイベントやoffer/answer作成後にRTCPeerConnectionから取得した自分自身のSDPやICE候補もサーバーに送信します。
Signaling Serverは、SDP(Session Description Protocol)とICE候補の2つの主要なメタデータを伝送します。SDPはメディアストリームのパラメータ(コーデック、サンプリングレート、チャンネル数、伝送方向) を記述します。ICE候補には、ピアが接続のためにアクセス可能なネットワークアドレス(ローカル、STUNから取得、TURNからのリレー)が含まれます。
SDPは、セッションおよびメディアセクションを含むテキスト形式で提供されます。セッション部分は一般的なパラメータ(セッションID、バージョン、名称)を記述し、メディアセクションは各メディアストリーム(音声、動画、DataChannel)をコーデック、ポート、プロトコルとともに記述します。ICE候補にはfoundation(グルーピング識別子)、優先度、IPアドレス、ポート、タイプ(host、srflx、relay)、プロトコル(UDP、TCP)が含まれます。各候補には、それを特定のICEプロセスに結びつけるufrag(ユーザー名フラグメント)属性も含まれています。
Trickle ICEは、WebRTC接続確立を大幅に加速します。すべてのICE候補の完全な収集を待つかわりに(複雑なネットワークで2–10秒かかることがあります)、各候補は発見後すぐにSignaling Serverに送信されます。リモートピアは候補を受け取り、ICEフレームワークにより直ちに接続テストを開始します。これにより、大多数の場合で接続確立時間が500–1500msに縮小されます。
よくある質問
Signaling Server — コール前の「コーディネーター」です。2つのデバイスが互いを見つけ、どのように通信するかを合意するのを手伝っています。デバイスが「知り合い」合意した後は、サーバーは不要になり、直接通信します。
WebRTCはシグナリングプロトコルを定義せず、開発者が最も適したトランスポートを選べるようにしています。ブラウザには他のユーザーを発見する組み込み機構はなく、この仕事はSignaling Serverが行います。サーバーは「郵便配達局」として動き、コール参加者間で招待や接続設定を届けます。
WebSocketが、大多数のWebアプリに最適です:全双方向、ブラウザがネイティブにサポート、実装が簡単。既存のVoIPインフラとの統合にはSIPを選んでください。機能豊かなチャットアプリにはXMPP、IoTシナリオにはMQTTを使用します。
スケーリングには、Redis Pub/SubやKafkaを使用した水平スケーリングが一般的です。各サーバーインスタンスが自分のWebSocket接続を処理し、サーバー間ルーティングに共通データバスが使用されます。このアプローチにより、数百万の同時シグナリングセッションを処理できます。
Signaling Serverは接続確立階段でのみ重要です。サーバーが一時的に利用不可になっても、アクティブなWebRTCコールは続行し、メディアトラフィックはピア間で直接流れます。問題は新しい接続を試みるときにのみ生じます。可靠性のために、サーバークラスタリングとバックアップシグナリングチャネルを使用してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。