WebTransport の使用

WebTransport は、低レイテンシの双方向のクライアント サーバー メッセージングを提供する API です。そのユースケースと、実装の未来についてフィードバックを行う方法の詳細をご覧ください。

背景

WebTransport とは

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

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

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

ユースケース

これは、デベロッパーが WebTransport を使用できる方法の簡単なリストです。

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

WebTransport の活用方法について、詳しくお聞かせいただきたく、ご連絡を差し上げました。

ブラウザ サポート

対応ブラウザ

  • Chrome: 97。 <ph type="x-smartling-placeholder">
  • Edge: 97。 <ph type="x-smartling-placeholder">
  • Firefox: 114。 <ph type="x-smartling-placeholder">
  • Safari: サポートされていません。 <ph type="x-smartling-placeholder">

ソース

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

現在のステータス

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

WebTransport と他の技術の関係

WebTransport は WebSocket の代わりとなるのですか?

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

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

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

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 互換サーバーを実行する場合、必要な設定と構成は WebRTC サーバーの管理よりも少なくて済みます。WebRTC では、トランスポートを機能させるために複数のプロトコル(ICEDTLSSCTP)を理解する必要があります。WebRTC には多くの変動要素が伴い、クライアントとサーバーのネゴシエーションの失敗につながる可能性があります。

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

ただし、すでに問題なく動作する 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 インターフェースの更新について通知されます。

ポリフィル

という Polyfill(または、利用可能なスタンドアロン モジュールとして機能を提供するポニーフィル)です。 webtransport-ponyfill-websocket WebTransport の一部の機能を実装しています。次のスライドに示す制約については、 プロジェクトの README を確認して、このソリューションがユースケースに適しているかどうかを判断します。

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

信頼できるガイダンスについては、仕様案の対応するセクションをご覧ください。

フィードバック

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

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

この API について何か問題や想定どおりに機能しない点はありますか?あるいは、アイデアを形にするために不足している部分はありますか?

Web Transport GitHub リポジトリで問題を提出するか、既存の問題に自分の考えを追加します。

実装に問題がある場合

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

https://new.crbug.com でバグを報告します。再現のための簡単な手順とともに、できるだけ詳しい情報を含めてください。

API の使用をご計画の場合は、

皆様の公開サポートは、Chrome の機能の優先順位付けに役立ち、他のブラウザ ベンダーにそれらのサポートの重要性を伝えます。

  • ハッシュタグを使用して @ChromiumDev にツイートしてください #WebTransport どこでどのように使用しているかの詳細です

全般的なディスカッション

他のカテゴリに当てはまらない一般的な質問や問題については、web-transport-dev Google グループをご利用ください。

謝辞

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

この投稿のヒーロー画像は Robin Pierre が Unsplash に投稿しました。