WebTransport の使用

WebTransport は、低レイテンシの双方向クライアント サーバー メッセージングを提供する API です。ユースケースと、実装の将来についてのフィードバックを送信する方法について説明します。

背景

WebTransport とは

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

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

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

ユースケース

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

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

WebTransport の使用計画について、詳しくお聞かせください

ブラウザ サポート

Browser Support

  • Chrome: 97.
  • Edge: 97.
  • Firefox: 114.
  • Safari: behind a flag.

Source

ブラウザで普遍的にサポートされていないすべての機能と同様に、機能検出を使用して防御的にコーディングすることをおすすめします。

現在のステータス

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

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

WebTransport は WebSockets の代替ですか?

可能であるとは言いきれません。WebSockets または WebTransport のいずれかが有効な通信プロトコルとなるユースケースがあります。

WebSocket 通信は、単一の信頼性の高い順序付けされたメッセージ ストリームを中心にモデル化されています。これは、一部のタイプの通信ニーズには適しています。これらの特性が必要な場合は、WebTransport のストリーム API でも提供できます。一方、WebTransport のデータグラム API は、信頼性や順序付けの保証なしで低遅延配信を提供するため、WebSocket の直接的な代替にはなりません。

WebTransport を使用すると、データグラム API または複数の同時 Streams API インスタンスを介して、WebSockets で問題となる可能性があるヘッドオブライン ブロッキングを心配する必要がなくなります。また、新しい接続を確立する際に、基盤となる QUIC ハンドシェイクは TLS 経由の TCP の起動よりも高速であるため、パフォーマンス上のメリットもあります。

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

WebTransport は UDP ソケット API と同じですか?

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

WebTransport は WebRTC データチャネルの代替手段ですか?

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

一般的に、HTTP/3 互換サーバーの実行には、WebRTC サーバーの維持よりも少ない設定と構成が必要です。WebRTC サーバーの維持には、動作するトランスポートを取得するために、複数のプロトコル(ICEDTLSSCTP)を理解する必要があります。WebRTC には、クライアントとサーバーのネゴシエーションの失敗につながる可能性のある多くの可動部分が含まれています。

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

ただし、すでに満足のいく WebRTC クライアント/サーバーのセットアップが機能している場合は、WebTransport に切り替えてもメリットはあまりないかもしれません。

試してみる

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

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

API の使用

WebTransport は、Streams API などの最新のウェブ プラットフォーム プリミティブの上に設計されています。Promise に大きく依存しており、asyncawait でうまく動作します。

Chromium の現在の WebTransport 実装では、データグラム、一方向ストリーム、双方向ストリームの 3 種類のトラフィックをサポートしています。

サーバーへの接続

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

ready Promise を使用して、接続が確立されるまで待機する必要があります。この Promise は、セットアップが完了するまで履行されず、QUIC/TLS ステージで接続が失敗した場合は拒否されます。

closed プロミスは、接続が正常に閉じられたときに履行され、予期しない終了の場合は拒否されます。

クライアントの指示エラー(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.t>hen(() = {
  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;

データグラム 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 です。データグラム 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 プロミスを使用すると、ストリームのクローズを検出できます。基盤となる HTTP/3 ストリームが FIN ビットで閉じられると、すべてのデータが読み取られた後に closed プロミスが履行されます。HTTP/3 ストリームが突然閉じられた場合(RESET_STREAM など)、closed プロミスは拒否されます。

// Assume an active receiveStream
const reader = receiveStream.readable.getReader();
reader.closed.then(() => {
  console.log('The receiveStream closed gracefully.');
}).ca>tch(() = {
  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

サーバーによって作成された WebTransportBidirectionalStream をリッスンするには、WebTransport インスタンスの incomingBidirectionalStreams 属性を使用します。この属性は 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
}

WebTransportBidirectionalStreamWebTransportSendStreamWebTransportReceiveStream の組み合わせです。前の 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 の説明仕様のドラフト関連する設計ドキュメントの情報が組み込まれています。この基盤を提供してくださった各著者に感謝いたします。

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