WebTransport の使用

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

背景

WebTransport とは何ですか?

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

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

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

ユースケース

以下に、デベロッパーが WebTransport を使用する可能性のある方法をいくつか示します。

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

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

ブラウザ サポート

対応ブラウザ

  • Chrome: 97.
  • Edge: 97.
  • Firefox: 114.
  • Safari: サポートされていません。

ソース

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

現在のステータス

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

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

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

必要な場合もある。WebSockets または WebTransport のいずれかが有効な通信プロトコルとして使用できるユースケースがあります。

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

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

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

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

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

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

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

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

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

ただし、すでに動作する WebRTC クライアント/サーバーの設定があり、満足している場合は、WebTransport に切り替えても多くのメリットが得られない可能性があります。

試してみる

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

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

API の使用

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

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

サーバーに接続する

HTTP/3 サーバーに接続するには、WebTransport インスタンスを作成します。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.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 インスタンスを使用すると、datagramsと呼ばれる個別のビットデータを送受信できます。

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

ウェブクライアントは、WebTransport インスタンスの createUnidirectionalStream() メソッドを使用して WebTransportSendStream を作成し、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.');
}).catch(() => {
  console.error('The receiveStream closed abruptly.');
});

WebTransportBidirectionalStream

WebTransportBidirectionalStream は、サーバーまたはクライアントによって作成される場合があります。

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

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 の一部の機能を実装する polyfill(または、使用できるスタンドアロン モジュールとして機能を提供する ponyfill)である 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 によるものです。