WebTransport API 提供低延遲的雙向用戶端/伺服器訊息傳輸功能。進一步瞭解這項技術的用途,以及如何提供意見回饋,協助我們改善實作方式。
背景
什麼是 WebTransport?
WebTransport 是一種網路 API,使用 HTTP/3 通訊協定做為雙向傳輸。這項功能適用於網頁用戶端與 HTTP/3 伺服器之間的雙向通訊。它支援透過 datagram API 不可靠地傳送資料,以及透過 streams API 可靠地傳送資料。
資料包非常適合傳送及接收不需要強效傳送保證的資料。資料封包的大小會受到基礎連線最大傳輸單位 (MTU) 的限制,而且可能無法順利傳輸,即使傳輸成功,抵達順序也可能任意。因此,資料電報 API 非常適合用於低延遲、盡可能傳輸資料的用途。您可以將資料包視為使用者資料包通訊協定 (UDP) 訊息,但經過加密且受到壅塞控制。
相較之下,串流 API 提供可靠的有序資料傳輸。如果您需要傳送或接收一或多個有序資料串流,非常適合使用這個選項。使用多個 WebTransport 串流,就如同建立多個 TCP 連線,但由於 HTTP/3 在幕後使用較輕量的 QUIC 通訊協定,因此開啟和關閉這些連線時,不會造成太多負擔。
用途
以下列舉開發人員可能使用 WebTransport 的幾種方式。
- 透過小型、不可靠的亂序訊息,以最短延遲時間定期將遊戲狀態傳送至伺服器。
- 接收伺服器推送的媒體串流,延遲時間極短,且不受其他資料串流影響。
- 在網頁開啟時接收伺服器推送的通知。
我們很想進一步瞭解您打算如何使用 WebTransport。
瀏覽器支援
與所有不支援通用瀏覽器的功能一樣,透過功能偵測進行防禦性編碼是最佳做法。
目前狀態
步驟 | 狀態 |
---|---|
1. 建立說明 | 完成 |
2. 草擬規格初稿 | 完成 |
3. 收集意見回饋並反覆修正設計 | 完成 |
4. 來源試用 | 完成 |
5. 推出 | Chromium 97 |
WebTransport 與其他技術的關係
WebTransport 可以取代 WebSocket 嗎?
不一定。在某些情況下,WebSockets 或 WebTransport 都是有效的通訊協定。
WebSockets 通訊是根據單一、可靠且排序的訊息串流建立模型,這對於某些類型的通訊需求來說很合適。如果您需要這些特性,WebTransport 的串流 API 也可提供。相較之下,WebTransport 的資料電報 API 提供低延遲傳輸,但不保證可靠性或順序,因此無法直接取代 WebSocket。
透過資料電報 API 或多個並行 Streams API 執行個體使用 WebTransport,表示您不必擔心行首封鎖,這可能是 WebSockets 的問題。此外,建立新連線時,由於基礎 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 相容伺服器所需的設定和配置,比維護 WebRTC 伺服器少。維護 WebRTC 伺服器時,您必須瞭解多種通訊協定 (ICE、DTLS 和 SCTP),才能取得可運作的傳輸。WebRTC 涉及更多活動部分,可能導致用戶端/伺服器交涉失敗。
WebTransport API 的設計宗旨是滿足網頁開發人員的使用需求,因此與使用 WebRTC 的資料通道介面相比,更像是編寫現代網頁平台程式碼。與 WebRTC 不同,WebTransport 支援 Web Worker,因此您可以獨立於特定 HTML 網頁執行用戶端與伺服器之間的通訊。由於 WebTransport 公開了符合 Streams 的介面,因此支援與反向壓力相關的最佳化。
不過,如果您已擁有滿意的 WebRTC 用戶端/伺服器設定,切換至 WebTransport 可能不會帶來太多優勢。
立即試用
如要試用 WebTransport,最好的方法是啟動相容的 HTTP/3 伺服器。接著,您可以使用這個網頁和基本 JavaScript 用戶端,試用用戶端/伺服器通訊。
此外,您也可以使用社群維護的 Echo 伺服器:webtransport.day。
使用 API
WebTransport 是以現代化網頁平台基本元素 (例如 Streams API) 為基礎設計而成,這項功能極度仰賴Promise,且能與 async
和 await
搭配運作。
Chromium 目前的 WebTransport 實作項目支援三種不同的流量類型:資料包,以及單向和雙向串流。
連線至伺服器
您可以建立 WebTransport
執行個體,連線至 HTTP/3 伺服器。網址的通訊協定應為 https
。您必須明確指定通訊埠編號。
您應使用 ready
Promise 等待連線建立。設定完成後,系統才會兌現這項 Promise,如果連線在 QUIC/TLS 階段失敗,系統會拒絕這項 Promise。
連線正常關閉時,closed
promise 會完成;如果連線意外關閉,則會遭到拒絕。
如果伺服器因用戶端指標錯誤 (例如網址路徑無效) 而拒絕連線,就會導致 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
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);
}
Streams API
連線至伺服器後,您也可以使用 WebTransport 的 Streams API 傳送及接收資料。
所有串流的每個區塊都是 Uint8Array
。與 Datagram API 不同,這些串流是可靠的。但每個串流都是獨立的,因此無法保證串流間的資料順序。
WebTransportSendStream
網路用戶端會使用 WebTransport
例項的 createUnidirectionalStream()
方法建立 WebTransportSendStream
,該方法會傳回 WebTransportSendStream
的 Promise。
使用 WritableStreamDefaultWriter
的 close()
方法關閉相關聯的 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: ${erro
r}`);
}
同樣地,請使用 WritableStreamDefaultWriter
的 abort()
方法,將 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);
}
您可以使用 ReadableStreamDefaultReader
的 closed
Promise 偵測串流關閉。當基礎 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 abrup
tly.');
});
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
只是 WebTransportSendStream
和 WebTransportReceiveStream
的組合。前兩節的範例說明如何使用這兩種方法。
其他示例
WebTransport 草案規格包含許多額外的內嵌範例,以及所有方法和屬性的完整說明文件。
Chrome 開發人員工具中的 WebTransport
很抱歉,Chrome 開發人員工具目前不支援 WebTransport。您可以「加上星號」這個 Chrome 問題,以便在開發人員工具介面有更新時收到通知。
Polyfill
我們提供名為 webtransport-ponyfill-websocket
的 Polyfill (或更確切地說,是提供功能做為獨立模組的 Ponyfill),可實作 WebTransport 的部分功能。請仔細閱讀專案的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 提供。