使用 WebTransport

WebTransport 是提供低延遲、雙向用戶端與用戶端訊息傳遞的 API。進一步瞭解這項功能的用途,以及如何針對日後的導入作業提供意見回饋。

背景

什麼是 WebTransport?

WebTransport 是採用 HTTP/3 通訊協定做為雙向傳輸的網路 API。適用於網路用戶端與 HTTP/3 伺服器之間的雙向通訊。透過其 datagram API 以不穩定的方式傳送資料,並透過串流 API 穩定可靠地傳送資料。

資料元適合用於傳送及接收不需要嚴格傳送保證的資料。個別資料封包的大小受限於基礎連線的最大傳輸單位 (MTU),因此不一定會成功傳輸,如果傳送失敗,封包可能會以任意順序送達。這些特性讓資料元 API 非常適合用於低延遲、最佳的資料傳輸。資料元可視為使用者資料包通訊協定 (UDP) 訊息,但受到加密及擁塞控制。

相反地,串流 API 則提供可靠的已排序資料移轉作業。這種方式非常適合用於傳送或接收一或多個已排序資料的串流。使用多個 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 的串流 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 提供與 Streams 相容的介面,因此支援有關背壓的最佳化作業。

不過,如果您很滿意 WebRTC 用戶端/伺服器設定,那麼切換到 WebTransport 可能會帶來許多好處。

立即試用

如要實驗 WebTransport,最好的方法就是啟動相容的 HTTP/3 伺服器。 接下來,您就能透過基本 JavaScript 用戶端使用這個網頁,嘗試用戶端/伺服器之間的通訊。

此外,webtransport.day 還提供由社群維護的 echo 伺服器。

使用 API

WebTransport 是以新型網路平台基本元素 (例如 Streams API) 為基礎而設計。這項服務非常仰賴承諾,且適用於 asyncawait

Chromium 目前實作的 WebTransport 支援三種不同的流量類型:資料元和單向串流與雙向串流。

連線至伺服器

建立 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 執行個體後,即可使用它來傳送及接收不同的資料,稱為「datagrams」

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

網路用戶端使用 WebTransport 例項的 createUnidirectionalStream() 方法建立 WebTransportSendStream,該方法會傳回 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 問題:我們會在開發人員工具介面更新時通知你。

聚合物

Polyfill (或做為功能做為獨立模組可使用的 ponyfill),稱為 webtransport-ponyfill-websocket敬上 實作 WebTransport 部分功能的例子。詳閱 專案的 README,判斷這個解決方案是否適用於您的用途。

隱私權和安全性考量

如需具公信力的指引,請參閱草稿規格的相應章節

意見回饋

Chrome 團隊想聽聽您對這個 API 的想法和使用經驗。

對 API 設計提供意見

您認為這個 API 是否有任何問題或無法正常運作?還是有什麼事情需要實現?

前往 Web Transport GitHub 存放區提出問題,或是將您的想法新增至現有問題。

無法導入嗎?

您發現 Chrome 實作錯誤嗎?

前往 https://new.crbug.com 回報錯誤。請盡可能附上所有詳細資料,以及簡單的重製說明。

想要使用這個 API 嗎?

你的公開支援服務可幫助 Chrome 優先開發特定功能,並說明其他瀏覽器廠商對於這些功能的重要性。

一般討論

您可以使用 web-transport-dev Google 網路論壇處理一般問題或無法歸咎於其他任何類別的問題。

特別銘謝

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

本文中的主頁橫幅是由 Robin Pierre 提供的 Unsplash 上提供。