使用 WebTransport

WebTransport 是一款 API,提供低延遲、雙向的用戶端伺服器訊息傳遞服務。進一步瞭解用途,以及如何針對實作方式的未來提供意見。

背景

什麼是 WebTransport?

WebTransport 是網路 API,會使用 HTTP/3 通訊協定做為雙向傳輸。而是用於網路用戶端與 HTTP/3 伺服器之間的雙向通訊。這項服務支援透過 Datagram API 以不可靠的方式傳送資料,也可透過其串流 API 穩定傳送資料。

Datagram 適合用來傳送和接收不需要嚴格傳送保證的資料。資料的個別封包大小受限於基礎連線的最大傳輸單位 (MTU) 大小,而且不會成功傳輸;如果傳輸,封包可能會以任意順序送達。這些特性使 Datagram API 非常適合提供低延遲、最佳的資料傳輸。資料語法就像使用者資料包通訊協定 (UDP) 訊息,但經過加密與管制。

相較之下,串流 API 則提供可靠的排序資料移轉。這些解決方案非常適合需要傳送或接收一或多個已排序資料的串流的情況。使用多個 WebTransport 串流類似建立多個 TCP 連線,但由於 HTTP/3 會在背景使用較輕量的 QUIC 通訊協定,因此開啟和關閉這些串流無需過多負擔。

應用情境

以下清單列出開發人員可能會如何使用 WebTransport,

  • 透過不穩定、脫序的小型訊息定期傳送遊戲狀態,盡可能縮短延遲時間。
  • 接收由伺服器推送的媒體串流,而且在延遲時間最短的情況下,不受其他資料串流影響。
  • 在網頁開啟時接收由伺服器推送的通知。

我們希望進一步瞭解您計劃使用 WebTransport 的方式,

瀏覽器支援

瀏覽器支援

  • 97
  • 97
  • 114
  • x

資料來源

就像所有不支援通用瀏覽器的功能一樣,使用功能偵測功能防禦編碼是最佳做法。

目前狀態

步驟 狀態
1. 建立說明 完成
2. 建立規格的初始草稿 完成
3. 收集意見回饋並改進設計 完整
4. 來源試用 完整
5. 啟動 Chromium 97

WebTransport 與其他技術的關係

WebTransport 會取代 WebSocket 嗎?

不一定。在某些情況下,WebSockets 或 WebTransport 可能是有效的通訊通訊協定。

WebSocket 通訊是以單一、可靠、有序的串流訊息流為基礎,這對某些通訊需求來說很實用。如果您需要這些特性,WebTransport 的串流 API 也可以提供這些特性。相較之下,WebTransport 的 Datagram API 提供低延遲的傳送功能,而且無法保證可靠性或排序,因此不會直接取代 WebSocket。

使用 WebTransport、透過 Datagram API 或透過多個並行 Streams API 執行個體的方式,就是您不必擔心前行封鎖的問題,這可能是 WebSocket 的問題。此外,建立新的連線時也會對效能有所助益,因為基礎 QUIC 握手的速度比透過 TLS 啟動 TCP 更快。

WebTransport 是新草稿規格的一部分,因此,用戶端和伺服器程式庫的 WebSocket 生態系統目前已經更加完善可靠。如果您需要具備常見伺服器設定且「立即可用」的功能,並支援廣泛的網路用戶端,WebSocket 是目前更好的選擇。

WebTransport 是否與 UDP Socket API 相同?

不可以。WebTransport 不是 UDP Socket API。雖然 WebTransport 使用 HTTP/3,因此其「後端」使用 UDP,但 WebTransport 針對加密和壅塞控制項的規定,因此比基本的 UDP Socket API 還多。

WebTransport 是 WebRTC 資料管道的替代方案嗎?

是,用於用戶端與伺服器的連線。WebTransport 使用許多與 WebRTC 資料管道相同的屬性,不過基礎通訊協定各不相同。

一般來說,相較於維護 WebRTC 伺服器,執行與 HTTP/3 相容的伺服器所需的設定和設定較少,必須瞭解多個通訊協定 (ICEDTLSSCTP) 才能確保傳輸中的傳輸作業。WebRTC 需要有更多移動項目,可能會導致用戶端/伺服器協商失敗。

WebTransport API 是專為網頁程式開發人員所設計,因此相較於使用 WebRTC 的資料管道介面,更應像編寫新型網路平台程式碼一般。與 WebRTC 不同Web Worker 內支援 WebTransport,因此可讓你獨立於特定 HTML 網頁,執行用戶端與伺服器通訊。由於 WebTransport 提供與串流相容的介面,因此支援背壓最佳化功能。

不過,如果您已設定可正常運作的 WebRTC 用戶端/伺服器設定,改用 WebTransport 可能不會提供許多好處。

立即體驗

如要測試 WebTransport,最好的方法就是啟動相容的 HTTP/3 伺服器。接著您可以搭配基本 JavaScript 用戶端使用這個頁面,嘗試用戶端/伺服器通訊。

此外,您也可以透過 webtransport.day 使用社群維護的 echo 伺服器。

使用 API

WebTransport 是以新型網路平台基本功能為基礎,例如 Streams API。智慧出價相當仰賴承諾,並與 asyncawait 搭配運作。

Chromium 目前導入的 WebTransport 功能支援三種不同的流量類型:Datagram,以及單向與雙向串流。

連線至伺服器

只要建立 WebTransport 執行個體,即可連線至 HTTP/3 伺服器。網址配置應為 https。您必須明確指定通訊埠編號。

您應使用 ready 承諾來等待連線建立完成。設定程序完成後才能實現這項承諾。如果連線在 QUIC/TLS 階段失敗,將會拒絕連線。

連線正常關閉時,closed 保證會執行完成,如果車輛意外關閉,則拒絕。

如果伺服器因「用戶端指示」錯誤 (例如網址路徑無效) 而拒絕連線,則會導致 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 getter 會傳回 WritableStream,可供網路用戶端用來將資料傳送至伺服器。readable getter 會傳回 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);
}

串流 API

連線至伺服器後,您也可以使用 WebTransport,透過其 Streams API 傳送及接收資料。

所有串流的每個區塊都是 Uint8Array。與 Datagram API 不同的是,這些串流是可靠的。不過,每個串流各自獨立,因此不保證各個串流的資料順序。

WebTransportSendStream

WebTransportSendStream 是由網路用戶端使用 WebTransport 執行個體的 createUnidirectionalStream() 方法建立,該方法會為 WebTransportSendStream 傳回承諾。

使用 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。首先,它會呼叫 WebTransport 例項的 incomingUnidirectionalStreams 屬性,該屬性會傳回 ReadableStream。而該 ReadableStream 的每個區塊反過來就是一個 WebTransportReceiveStream,可用來讀取伺服器傳送的 Uint8Array 執行個體。

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 傳回承諾。

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 的組合。在前兩節的例子中,我們將說明每個區段的使用方式。

其他示例

WebTransport 草稿規格包含幾個其他內嵌範例,以及所有方法和屬性的完整說明文件。

Chrome 開發人員工具中的 WebTransport

很抱歉,Chrome 的開發人員工具目前不支援 WebTransport。只要為這個 Chrome 問題加上星號,即可在開發人員工具介面有更新時收到通知。

聚酯纖維

名為 webtransport-ponyfill-websocket 的 Polyfill (或簡稱 填充,可提供功能做為獨立模組可供使用),可以實作 WebTransport 的部分功能。請詳閱專案的 README 中的限制,判斷這個解決方案是否適合您的用途。

隱私權和安全性考量

請參閱規格草稿的對應章節,取得權威指引。

意見回饋:

Chrome 團隊希望瞭解你對這個 API 的想法和使用經驗。

API 設計相關意見

API 是否發生了故障或無法正常運作的問題?或者,你要實現構想的某些部分缺少了什麼?

前往網路傳輸 GitHub 存放區提出問題,或是新增想法至現有的問題。

實作時遇到問題嗎?

您在執行 Chrome 時發現錯誤了嗎?

前往 https://new.crbug.com 回報錯誤。請盡可能提供詳細資訊,並附上重現資料的操作說明。

打算使用 API 嗎?

您的公開支持可協助 Chrome 決定各項功能的優先順序,並向其他瀏覽器廠商瞭解這項功能有多重要。

一般討論

針對不屬於任何類別的一般問題,您可以使用 web-transport-dev Google 網路論壇提問。

特別銘謝

本文彙整了 WebTransport 說明草稿規格,以及相關設計文件的資訊。感謝個別作者提供該基礎。

這篇文章的主頁橫幅是 Robin Pierre 在 Unsplash 上提供的。