WebTransport の使用

WebTransport は、低レイテンシの双方向のクライアント サーバー メッセージングを提供する API です。ユースケースと、今後の実装に関するフィードバックの提供方法をご確認ください。

背景

WebTransport とは

WebTransport は、HTTP/3 プロトコルを双方向トランスポートとして使用するウェブ API です。これは、ウェブ クライアントと HTTP/3 サーバー間の双方向通信を対象としています。Datagram API を介した信頼性の低いデータ送信と、Streams API を介した信頼性の低いデータ送信の両方をサポートしています。

データグラムは、強力な配信保証を必要としないデータの送受信に最適です。個々のデータ パケットのサイズは、基盤となる接続の最大伝送単位(MTU)によって制限され、正常に送信される場合とそうでない場合があります。また、転送された場合、任意の順序で到着する可能性があります。これらの特性により、Datagram API は低レイテンシでベスト エフォートのデータ転送に最適です。データグラムはユーザー データグラム プロトコル(UDP)のメッセージと考えることができますが、暗号化され、輻輳制御されます。

一方、ストリーム API は、信頼性の高い、順序付けられたデータ転送を提供します。これは、順序付けされたデータの 1 つ以上のストリームを送受信する必要があるシナリオに適しています。複数の WebTransport ストリームを使用することは、複数の TCP 接続を確立することに似ていますが、HTTP/3 は内部で軽量の QUIC プロトコルを使用するため、それほど多くのオーバーヘッドなしで開閉できます。

ユースケース

これは、デベロッパーが WebTransport を使用する可能性がある方法のほんの一例です。

  • 小規模で信頼性の低い順不同のメッセージを介して、ゲームの状態を最小限のレイテンシでサーバーに送信する。
  • サーバーからプッシュされたメディア ストリームを、他のデータ ストリームとは別に最小限のレイテンシで受信する。
  • ウェブページが開いている間に、サーバーからプッシュされた通知を受信する。

Google では、WebTransport の利用計画について、詳細をお聞かせいただきたいと考えています

ブラウザ サポート

対応ブラウザ

  • 97
  • 97
  • 114
  • x

ソース

ブラウザのユニバーサル サポートがないすべての機能と同様に、機能検出による防御的なコーディングがベスト プラクティスです。

現在のステータス

ステップ ステータス
1. 説明を作成 完了
2. 仕様の最初のドラフトを作成する 完了
3. フィードバックを収集して設計を繰り返す 完了
4. オリジン トライアル 完了
5. リリース Chromium 97

WebTransport と他のテクノロジーの関係

WebTransport は WebSocket に代わるものですか?

可能であるとは言いきれません。ユースケースによっては、WebSockets または WebTransport が有効な通信プロトコルである場合もあります。

WebSocket の通信は、信頼性の高い、順序付けされた単一のメッセージ ストリームをモデルにしているため、通信のニーズによってはこれで十分です。これらの特性が必要な場合は、WebTransport のストリーム API でも使用できます。これに対して、WebTransport の Datagram API は低レイテンシで配信し、信頼性や順序は保証されないため、WebSocket の直接的な代替ではありません。

Datagram API、または複数の同時 Streams API インスタンスを介して WebTransport を使用すると、WebSocket の問題になり得るヘッドオブライン ブロッキングを心配する必要がなくなります。また、基盤となる QUIC handshake は TCP over TLS を起動するよりも高速であるため、新しい接続を確立する場合はパフォーマンス上の利点があります。

WebTransport は新しいドラフト仕様の一部であるため、クライアント ライブラリとサーバー ライブラリを中心とする WebSocket エコシステムは現在、はるかに堅牢です。一般的なサーバー設定ですぐに機能し、幅広いウェブ クライアント サポートを必要とする場合は、WebSockets の方が適しています。

WebTransport は UDP Socket API と同じですか?

いいえ。WebTransport は UDP Socket API ではありません。WebTransport は HTTP/3 を使用し、HTTP/3 は「内部で」UDP を使用しますが、WebTransport には暗号化と輻輳制御に関する要件があり、基本的な UDP Socket API を超えるものとなっています。

WebTransport は WebRTC データチャネルに代わるものですか?

○(クライアント サーバー接続の場合)WebTransport は WebRTC データチャネルと同じプロパティの多くを共有しますが、基盤となるプロトコルは異なります。

一般に、HTTP/3 互換サーバーを運用する場合、WebRTC サーバーを維持するよりもセットアップや構成が少なくて済みます。WebRTC の場合、トランスポートを正常に動作させるには、複数のプロトコル(ICEDTLSSCTP)について理解する必要があります。WebRTC では、クライアントとサーバーのネゴシエーションが失敗する原因となり得る、さらに多くの流動的な要素が必要です。

WebTransport API は、ウェブ デベロッパーのユースケースを念頭に置いて設計されているため、WebRTC のデータチャネル インターフェースを使用するよりも、最新のウェブ プラットフォーム コードを記述するように感じられます。WebRTC とは異なり、WebTransport はウェブ ワーカー内でサポートされているため、特定の HTML ページとは無関係にクライアントとサーバー間の通信を行うことができます。WebTransport は Streams 準拠のインターフェースを公開するため、バックプレッシャーに関する最適化をサポートしています。

ただし、すでに WebRTC クライアント/サーバーが正しく機能している設定に満足している場合は、WebTransport に切り替えても多くのメリットが得られるとは限りません。

試してみる

WebTransport をテストする最良の方法は、互換性のある HTTP/3 サーバーを起動することです。その後、このページを基本的な JavaScript クライアントで使用して、クライアント/サーバー通信を試すことができます。

また、コミュニティが管理するエコー サーバーは webtransport.day で利用できます。

API の使用

WebTransport は、Streams API などの最新のウェブ プラットフォーム プリミティブ上に設計されています。Promise に大きく依存し、async および await とうまく連携します。

Chromium に現在実装されている WebTransport は、データグラムと単方向ストリームと双方向ストリームの 3 種類のトラフィックに対応しています。

サーバーへの接続

HTTP/3 サーバーに接続するには、WebTransport インスタンスを作成します。URL のスキームは https にする必要があります。ポート番号を明示的に指定する必要があります。

接続が確立されるまで待つには、ready Promise を使用する必要があります。この Promise は設定が完了するまで実行されず、QUIC/TLS の段階で接続が失敗した場合は拒否されます。

closed Promise は、接続が正常に終了すると解決され、予期せぬ終了だった場合は拒否されます。

クライアント表示エラー(URL のパスが無効など)によりサーバーが接続を拒否した場合、closed は拒否され、ready は未解決のままになります。

const url = 'https://example.com:4999/foo/bar';
const transport = new WebTransport(url);

// Optionally, set up functions to respond to
// the connection closing:
transport.closed.then(() => {
  console.log(`The HTTP/3 connection to ${url} closed gracefully.`);
}).catch((error) => {
  console.error(`The HTTP/3 connection to ${url} closed due to ${error}.`);
});

// Once .ready fulfills, the connection can be used.
await transport.ready;

Datagram API

サーバーに接続した WebTransport インスタンスを取得したら、それを使用して、データグラムと呼ばれる個別のデータを送受信できます。

writeable ゲッターは WritableStream を返します。ウェブ クライアントは、これを使用してサーバーにデータを送信できます。readable ゲッターは ReadableStream を返します。これにより、サーバーからのデータをリッスンできます。どちらのストリームも本質的に信頼性が低いため、書き込んだデータがサーバーで受信されない、またはその逆の可能性がある。

どちらのタイプのストリームも、データ転送に Uint8Array インスタンスを使用します。

// Send two datagrams to the server.
const writer = transport.datagrams.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);

// Read datagrams from the server.
const reader = transport.datagrams.readable.getReader();
while (true) {
  const {value, done} = await reader.read();
  if (done) {
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

Streams API

サーバーに接続すると、WebTransport を使用して Streams API を介してデータを送受信することもできます。

すべてのストリームの各チャンクは Uint8Array です。Datagram API とは異なり、これらのストリームは信頼性に優れています。ただし、各ストリームは独立しているため、ストリーム間でのデータの順序は保証されません。

WebTransportSendStream

WebTransportSendStream は、WebTransport インスタンスの createUnidirectionalStream() メソッドを使用してウェブ クライアントによって作成され、WebTransportSendStream の Promise を返します。

WritableStreamDefaultWriterclose() メソッドを使用して、関連する HTTP/3 接続を閉じます。ブラウザは、関連する接続を実際に閉じる前に、保留中のデータをすべて送信しようとします。

// Send two Uint8Arrays to the server.
const stream = await transport.createUnidirectionalStream();
const writer = stream.writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);
try {
  await writer.close();
  console.log('All data has been sent.');
} catch (error) {
  console.error(`An error occurred: ${error}`);
}

同様に、WritableStreamDefaultWriterabort() メソッドを使用して、RESET\_STREAM をサーバーに送信します。abort() を使用している場合、ブラウザは、まだ送信されていない保留中のデータをすべて破棄することがあります。

const ws = await transport.createUnidirectionalStream();
const writer = ws.getWriter();
writer.write(...);
writer.write(...);
await writer.abort();
// Not all the data may have been written.

WebTransportReceiveStream

WebTransportReceiveStream がサーバーによって開始されます。ウェブ クライアントの場合、WebTransportReceiveStream の取得は 2 段階のプロセスです。まず、WebTransport インスタンスの incomingUnidirectionalStreams 属性を呼び出します。これにより、ReadableStream が返されます。ReadableStream の各チャンクは、さらに、サーバーから送信された Uint8Array インスタンスの読み取りに使用できる WebTransportReceiveStream です。

async function readFrom(receiveStream) {
  const reader = receiveStream.readable.getReader();
  while (true) {
    const {done, value} = await reader.read();
    if (done) {
      break;
    }
    // value is a Uint8Array
    console.log(value);
  }
}

const rs = transport.incomingUnidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is an instance of WebTransportReceiveStream
  await readFrom(value);
}

ストリームの終了は、ReadableStreamDefaultReaderclosed Promise を使用して検出できます。基盤となる HTTP/3 接続が FIN ビットで閉じられると、すべてのデータが読み取られた後に closed Promise が実行されます。HTTP/3 接続が(RESET\_STREAM などによって)突然閉じられた場合、closed Promise は拒否されます。

// Assume an active receiveStream
const reader = receiveStream.readable.getReader();
reader.closed.then(() => {
  console.log('The receiveStream closed gracefully.');
}).catch(() => {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

WebTransportBidirectionalStream は、サーバーまたはクライアントのいずれかによって作成されます。

ウェブ クライアントは、WebTransport インスタンスの createBidirectionalStream() メソッドを使用してこれを作成できます。このメソッドは WebTransportBidirectionalStream の Promise を返します。

const stream = await transport.createBidirectionalStream();
// stream is a WebTransportBidirectionalStream
// stream.readable is a ReadableStream
// stream.writable is a WritableStream

WebTransport インスタンスの incomingBidirectionalStreams 属性を使用して、サーバーによって作成された WebTransportBidirectionalStream をリッスンできます。これにより、ReadableStream が返されます。この ReadableStream の各チャンクは、今度は WebTransportBidirectionalStream です。

const rs = transport.incomingBidirectionalStreams;
const reader = rs.getReader();
while (true) {
  const {done, value} = await reader.read();
  if (done) {
    break;
  }
  // value is a WebTransportBidirectionalStream
  // value.readable is a ReadableStream
  // value.writable is a WritableStream
}

WebTransportBidirectionalStream は、単に WebTransportSendStreamWebTransportReceiveStream を組み合わせたものです。前の 2 つのセクションの例では、それぞれの使用方法を説明します。

その他の例

WebTransport のドラフト仕様には、追加のインラインの例のほか、すべてのメソッドとプロパティの完全なドキュメントが含まれています。

Chrome の DevTools の WebTransport

残念ながら、現時点では Chrome の DevTools は WebTransport をサポートしていません。この Chrome の問題に「スター」を付けると、DevTools インターフェースの更新について通知を受け取ることができます。

ポリフィル

WebTransport の一部の機能を実装する webtransport-ponyfill-websocket というポリフィル(使用可能なスタンドアロン モジュールとして機能を提供するポニーフィル)を利用できます。プロジェクトの README の制約をよく読み、このソリューションがユースケースに適しているかどうかを判断してください。

プライバシーとセキュリティに関する考慮事項

正式なガイダンスについては、仕様ドラフトの対応するセクションをご覧ください。

フィードバック

Chrome チームでは、この API を使用する皆様のご意見やご感想をお待ちしています。

API 設計に関するフィードバック

API について、何かおかしい点や、期待どおりに動作しない点はありますか。あるいは、アイデアを実現するために欠けている要素はありませんか?

Web Transport GitHub リポジトリで問題を報告するか、既存の問題にご意見をお寄せください。

実装に問題がある場合

Chrome の実装にバグが見つかりましたか?

https://new.crbug.com でバグを報告します。再現するための簡単な手順とともに、できる限り詳細を記載します。

API を使用する予定がある場合は、

一般公開のサポートにより、Chrome で機能の優先度が下がるとともに、他のブラウザ ベンダーがそれらの機能をサポートすることがいかに重要であるかがわかります。

  • ハッシュタグ #WebTransport と、その使用場所や使用方法の詳細を使用して、@ChromiumDev にツイートを送信してください。

全般的な話題

他のどのカテゴリにも当てはまらない一般的な質問や問題には、web-transport-dev Google グループを使用できます。

謝辞

この記事には、WebTransport Explainerドラフト仕様関連する設計ドキュメントの情報を盛り込んでいます。基盤を提供してくれたそれぞれの作成者に感謝します。

この投稿のヒーロー画像は、Unsplash の Robin Pierre 氏によるものです。