能力 · API
WebTransport API
发布于 更新于
WebTransport 在客户端与服务器之间打开一条基于 HTTP/3(QUIC)的会话,并在其上复用两类流量:有序、可靠的字节流,页面通过 WHATWG Streams 读写;以及无序、不可靠的数据报,适合「最新一条覆盖上一条」的数据。当队头阻塞或 TCP 重传成为问题时,它是 WebSocket 的标准化替代方案,带 [SecureContext],并可在 Worker 中使用。
Chrome 97 在桌面、Android 和 WebView 上发布,Edge 97 与 Samsung Internet 18 跟进,Firefox 114 加入,Safari 26.4 把它带到 macOS 和 iOS(BCD api.WebTransport)。congestionControl 选项由 Firefox 114 和 Safari 26.4 实现,Chrome 未实现,接受该成员但忽略它。
new WebTransport(url)new WebTransport(url, options)
await transport.readytransport.closed
transport.createBidirectionalStream()transport.createBidirectionalStream(options)transport.createUnidirectionalStream()transport.createUnidirectionalStream(options)transport.incomingBidirectionalStreamstransport.incomingUnidirectionalStreamstransport.datagrams.readabletransport.datagrams.writable
transport.close()transport.close(closeInfo)构造函数同步返回并开始建连;ready 是会话建立后兑现的 Promise,closed 在正常关闭时以 WebTransportCloseInfo 兑现、会话失败时以 WebTransportError 拒绝。createBidirectionalStream() 以 WebTransportBidirectionalStream 兑现,其 readable 为 WebTransportReceiveStream、writable 为 WebTransportSendStream;createUnidirectionalStream() 以 WebTransportSendStream 兑现。两个 incoming* 属性是由服务器发起的流组成的 ReadableStream。
构造函数接受服务器 URL 和可选的 WebTransportOptions 字典;close() 接受可选的 WebTransportCloseInfo。每个选项都有默认值。
| 成员 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
USVString |
是 | HTTP/3 端点的绝对或相对 URL,按文档的 API base URL 解析。协议必须是 https,且不能带 fragment。 |
options.allowPooling |
boolean |
否 | 默认 false。为 true 时允许会话复用到同一源已有的 HTTP/3 连接上,而不是另开一条专用连接。 |
options.requireUnreliable |
boolean |
否 | 默认 false。为 true 时禁止在 HTTP/3 建连失败后回退到 HTTP/2,从而保证 datagrams 是真正的 QUIC 数据报。 |
options.headers |
HeadersInit |
否 | 默认 {}。建立会话的 CONNECT 请求所附带的额外请求头。 |
options.serverCertificateHashes |
sequence<WebTransportHash> |
否 | 默认 []。{ algorithm: "sha-256", value: BufferSource } 条目;非空时浏览器只在某个哈希匹配时信任服务器,这让专用连接可以使用自签名证书。算法未知的哈希被忽略。 |
options.congestionControl |
WebTransportCongestionControl |
否 | "default"、"throughput" 或 "low-latency";仅为提示,浏览器没有对应算法时降为 "default"。 |
options.anticipatedConcurrentIncomingUnidirectionalStreams |
unsigned short? |
否 | 默认 null。告知浏览器预计有多少条服务器发起的单向流,以便设置流控上限。 |
options.anticipatedConcurrentIncomingBidirectionalStreams |
unsigned short? |
否 | 默认 null。同上,针对服务器发起的双向流。 |
options.protocols |
sequence<DOMString> |
否 | 默认 []。向服务器提供的应用协议名;协商结果从 transport.protocol 读回。 |
options.datagramsReadableType |
ReadableStreamType |
否 | 设为 "bytes" 时 datagrams.readable 成为支持 BYOB 读取器的字节流。 |
closeInfo.closeCode |
unsigned long |
否 | 默认 0。发给对端的应用错误码。 |
closeInfo.reason |
USVString |
否 | 默认 ""。发给对端的可读关闭原因。 |
构造函数同步抛出;创建流的方法返回被拒绝的 Promise。会话与流的失败以 WebTransportError 呈现,它是 DOMException 的子类,带 source("session" 或 "stream")和 streamErrorCode 属性(见 WebTransportError 接口(w3.org))。
| 异常 | 条件 |
|---|---|
SyntaxError |
构造函数在 url 解析失败、协议不是 https、带有 fragment,或 protocols 含重复项、空名称或超过 512 字节的名称时抛出。 |
NotSupportedError |
构造函数在 allowPooling 为 true 且 serverCertificateHashes 非空时抛出;证书固定需要专用连接。 |
InvalidStateError |
会话进入 "closed" 或 "failed" 后,createBidirectionalStream()、createUnidirectionalStream()、createSendGroup()、getStats() 和 exportKeyingMaterial() 拒绝或抛出,包括调用进行中会话失败的情况。把某条流的 sendGroup 设为另一个 transport 的分组同样抛出它。 |
TypeError |
createBidirectionalStream() 或 createUnidirectionalStream() 收到的 sendGroup 选项属于另一个 WebTransport。 |
QuotaExceededError |
流 ID 耗尽且调用传了 waitUntilAvailable: false;默认的 true 下 Promise 会等待。 |
RangeError |
exportKeyingMaterial() 的 label 或 context 超过 255 字节,或输出长度为 0 或超过实现上限(至少 4096)。 |
WebTransportError |
CONNECT 请求失败或握手被拒时,ready 与 closed 以 source: "session" 拒绝;被重置的流,其 readable 和 writable 以 source: "stream" 加对端的 streamErrorCode 出错。 |
每个示例都先检测构造函数,并给出浏览器缺少它或服务器不可达时代码的实际行为。
带 WebSocket 回退的连接
Section titled “带 WebSocket 回退的连接”构造前先检查全局对象,再在 try 中 await ready:服务器没有 HTTP/3、UDP 端口被拦、证书有问题,都会让 ready 以 WebTransportError 拒绝,而不是在构造函数中抛出。
async function connect(url) { if (typeof WebTransport === "undefined") { return { kind: "websocket", socket: new WebSocket(url.replace(/^https/, "wss")) }; } const transport = new WebTransport(url); try { await transport.ready; } catch (err) { console.warn(`WebTransport 失败(${err.name},source=${err.source}),改用 WebSocket`); return { kind: "websocket", socket: new WebSocket(url.replace(/^https/, "wss")) }; } transport.closed .then((info) => console.log(`已关闭:code ${info.closeCode} "${info.reason}"`)) .catch((err) => console.error(`会话失败:${err.message}`)); return { kind: "webtransport", transport };}WebSocket 路径没有数据报,也没有流之间的独立性,调用方应把 kind 当作能力标志,而不是假设两者行为一致。
用数据报发送游戏状态
Section titled “用数据报发送游戏状态”数据报的上限是 transport.datagrams.maxDatagramSize 字节,由 QUIC 路径 MTU 决定,且可能丢失或乱序,所以每条消息必须自足。用数据报 writer 写,用循环读 datagrams.readable。
async function runDatagrams(transport, encodeState, applyState) { const writer = transport.datagrams.writable.getWriter(); const reader = transport.datagrams.readable.getReader(); const max = transport.datagrams.maxDatagramSize;
const timer = setInterval(async () => { const bytes = encodeState(); // 当前状态的类型化数组 if (bytes.byteLength <= max) await writer.write(bytes); }, 50);
try { while (true) { const { value, done } = await reader.read(); if (done) break; applyState(value); } } finally { clearInterval(timer); writer.releaseLock(); }}requireUnreliable 保持 false 而会话被池化到 HTTP/2 时,数据报会经回退通道可靠送达,延迟特性随之不同;不可靠语义比连通性更重要时,传 requireUnreliable: true。
在双向流上做回显并接收服务器发起的流
Section titled “在双向流上做回显并接收服务器发起的流”双向流是一对字节流;用 TextEncoderStream 和 TextDecoderStream 管道化,应用层看到的就是字符串。服务器发起的流到达 incomingBidirectionalStreams,必须循环读取,否则服务器的流预算会卡住。
async function echoOnce(transport, message) { let stream; try { stream = await transport.createBidirectionalStream(); } catch (err) { if (err.name === "InvalidStateError") return null; // 会话已关闭 throw err; } const writer = stream.writable.getWriter(); await writer.write(new TextEncoder().encode(message)); await writer.close();
const chunks = []; for await (const chunk of stream.readable.pipeThrough(new TextDecoderStream())) { chunks.push(chunk); } return chunks.join("");}
async function acceptServerStreams(transport, handle) { const reader = transport.incomingBidirectionalStreams.getReader(); while (true) { const { value: stream, done } = await reader.read(); if (done) break; handle(stream.readable, stream.writable); }}关闭 writer 会发送 FIN,回显服务器由此得知请求已完整;从不关闭的流会一直占用其 ID 直到会话结束。
- WebRTC API,其
RTCDataChannel是 WebTransport 流的点对点对应物 - WebCodecs,在通过 WebTransport 流发送媒体帧前先编码
- WebTransport: constructor(w3.org)
- WebTransport: WebTransportError interface(w3.org)
- WebTransport over HTTP/3(datatracker.ietf.org)
- Using WebTransport(developer.chrome.com)
规范
| 规范 | 状态 |
|---|---|
| WebTransport | W3C 草案 |
| WebTransport: constructor | W3C 草案 |
| WebTransport: createBidirectionalStream() | W3C 草案 |
| WebTransport: WebTransportError interface | W3C 草案 |
| WebTransport over HTTP/3 (IETF draft) | 其他 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Desktop) | 支持 | 97 | 高 | 来源 | — |
| Chrome (Android) | 支持 | 97 | 高 | 来源 | 1 |
| Edge (Desktop) | 支持 | 97 | 高 | 来源 | 2 |
| Firefox (Desktop) | 支持 | 114 | 高 | 来源 | — |
| Firefox (Android) | 支持 | 114 | 高 | 来源 | 3 |
| Safari (macOS) | 支持 | 26.4 | 高 | 来源 | — |
| Safari (iOS) | 支持 | 26.4 | 高 | 来源 | 4 |
| Samsung Internet | 支持 | 18.0 | 高 | 来源 | 5 |
| WebView (Android) | 支持 | 97 | 高 | 来源 | 6 |
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Chrome 的数据推导。
- 由 browser-compat-data 镜像自 Firefox 的数据推导。
- 由 browser-compat-data 镜像自 Safari 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。
- 由 browser-compat-data 镜像自 Chrome Android 的数据推导。