WebTransport 協定解析:基於 QUIC 的瀏覽器即時通訊方案
技术架构(更新於 2026年6月18日)
WebTransport 架構概覽
WebTransport 是基於 QUIC 協定的瀏覽器 API,提供低延遲、多路復用的雙向通訊能力。它執行在 HTTP/3 之上,天然繼承了 QUIC 的 0-RTT 握手、無隊頭阻塞等優勢。
應用層 WebTransport API
↓
傳輸層 QUIC (UDP)
↓
網路層 IP
對比傳統方案:
應用層 WebSocket API
↓
傳輸層 TCP → 隊頭阻塞
↓
網路層 IP
WebSocket vs WebTransport 對比
| 特性 | WebSocket | WebTransport |
|---|---|---|
| 傳輸協定 | TCP | QUIC (UDP) |
| 握手延遲 | 1-RTT (TCP) + 1-RTT (TLS) | 0-RTT (QUIC) |
| 隊頭阻塞 | ✅ 受影響 | ❌ 無隊頭阻塞 |
| 多路復用 | ❌ 單流 | ✅ 多流獨立 |
| 資料報支援 | ❌ 僅流式 | ✅ 資料報 + 流 |
| 可靠傳輸 | ✅ | 流模式 ✅ / 資料報 ❌ |
| 優先級 | ❌ | ✅ 流級優先級 |
| 連線遷移 | ❌ (新TCP) | ✅ (連線ID) |
| 瀏覽器支援 | 全平台 | Chrome 97+ |
兩種通訊模式
資料報模式(Datagram)
資料報提供不可靠、無序的訊息傳輸,適用於即時音視訊、遊戲位置同步等可容忍丟包的場景。
const transport = new WebTransport('https://server.example.com/chat');
await transport.ready;
const datagramWriter = transport.datagrams.writable.getWriter();
const datagramReader = transport.datagrams.readable.getReader();
await datagramWriter.write(new TextEncoder().encode('ping'));
const { value, done } = await datagramReader.read();
console.log(new TextDecoder().decode(value));
雙向流模式(Bidirectional Stream)
雙向流提供可靠、有序的位元組流傳輸,適用於檔案傳輸、聊天訊息、控制指令等不可丟包的場景。
const transport = new WebTransport('https://server.example.com/bidi');
await transport.ready;
const bidiStream = await transport.createBidirectionalStream();
const writer = bidiStream.writable.getWriter();
const reader = bidiStream.readable.getReader();
await writer.write(new TextEncoder().encode('可靠訊息'));
const { value } = await reader.read();
console.log(new TextDecoder().decode(value));
接收服務端發起的流
const transport = new WebTransport('https://server.example.com/push');
await transport.ready;
async function readIncomingStreams() {
const reader = transport.incomingBidirectionalStreams.getReader();
while (true) {
const { value: stream, done } = await reader.read();
if (done) break;
const dataReader = stream.readable.getReader();
const { value } = await dataReader.read();
console.log('服務端推送:', new TextDecoder().decode(value));
}
}
readIncomingStreams();
實戰:多人即時協作編輯
class CollaborativeTransport {
constructor(url) {
this.url = url;
this.transport = null;
this.streamMap = new Map();
}
async connect() {
this.transport = new WebTransport(this.url);
await this.transport.ready;
this.listenDatagrams();
this.listenIncomingStreams();
}
async listenDatagrams() {
const reader = this.transport.datagrams.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
this.handleCursorUpdate(value);
}
}
async listenIncomingStreams() {
const reader = this.transport.incomingBidirectionalStreams.getReader();
while (true) {
const { value: stream, done } = await reader.read();
if (done) break;
const userId = crypto.randomUUID();
this.streamMap.set(userId, stream);
this.readStream(userId, stream.readable);
}
}
async sendEditOperation(op) {
const datagram = new TextEncoder().encode(JSON.stringify(op));
await this.transport.datagrams.writable.getWriter().write(datagram);
}
async sendReliableMessage(userId, msg) {
const stream = this.streamMap.get(userId);
if (!stream) return;
const writer = stream.writable.getWriter();
await writer.write(new TextEncoder().encode(JSON.stringify(msg)));
writer.releaseLock();
}
handleCursorUpdate(data) {
const cursor = JSON.parse(new TextDecoder().decode(data));
document.querySelector(`[data-user="${cursor.userId}"]`)
?.style.transform = `translate(${cursor.x}px, ${cursor.y}px)`;
}
async readStream(userId, readable) {
const reader = readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
this.applyRemoteEdit(JSON.parse(new TextDecoder().decode(value)));
}
}
applyRemoteEdit(edit) {
console.log('應用遠端編輯:', edit);
}
async disconnect() {
await this.transport?.close();
this.streamMap.clear();
}
}
QUIC 協定核心特性
0-RTT 連線建立
首次連線(1-RTT):
Client → CH(Initial) → Server
Client ← SH, EE, CERT, CV, FIN ← Server
Client → Finished → Server
後續連線(0-RTT):
Client → CH(Initial) + 0-RTT Data → Server
Client ← SH, EE, CERT, CV, FIN + 1-RTT Data ← Server
連線遷移
// 網路切換(Wi-Fi → 4G)時 QUIC 透過連線 ID 保持連線
// TCP 方案需要重新三次握手 + TLS 協商
// WebTransport 基於 QUIC 連線 ID,無需重新握手
無隊頭阻塞
TCP (WebSocket):
Stream A: ████░░░░░░ ← Stream B 被阻塞
Stream B: ░░░░██████ ← 等待 Stream A 的丟失包
QUIC (WebTransport):
Stream A: ████░░░░░░ ← 獨立流
Stream B: ██████████ ← 不受 Stream A 影響
服務端實作(Node.js)
import { Http3WebTransportSession } from '@fails-components/webtransport';
const h3server = new Http3WebTransportSession({
cert: readFileSync('cert.pem'),
key: readFileSync('key.pem'),
port: 4433
});
h3server.on('session', (session) => {
console.log('WebTransport 會話建立');
session.on('datagram', (data) => {
const msg = JSON.parse(data.toString());
session.sendDatagram(Buffer.from(JSON.stringify({
type: 'ack',
echo: msg
})));
});
session.on('stream', (stream) => {
stream.on('data', (chunk) => {
const msg = JSON.parse(chunk.toString());
stream.write(Buffer.from(JSON.stringify({
type: 'response',
data: processMessage(msg)
})));
});
});
});
h3server.start();
流量控制與優先級
const transport = new WebTransport('https://server.example.com/');
await transport.ready;
const highPriorityStream = await transport.createBidirectionalStream();
const lowPriorityStream = await transport.createBidirectionalStream();
if ('sendOrder' in highPriorityStream.writable) {
highPriorityStream.writable.sendOrder = 100;
lowPriorityStream.writable.sendOrder = 1;
}
錯誤處理與重連
class ResilientTransport {
constructor(url, options = {}) {
this.url = url;
this.maxRetries = options.maxRetries ?? 3;
this.retryDelay = options.retryDelay ?? 1000;
this.transport = null;
}
async connect(retries = 0) {
try {
this.transport = new WebTransport(this.url);
await this.transport.ready;
console.log('WebTransport 連線成功');
this.monitorClose();
return this.transport;
} catch (err) {
if (retries < this.maxRetries) {
const delay = this.retryDelay * Math.pow(2, retries);
console.warn(`連線失敗,${delay}ms 後重試 (${retries + 1}/${this.maxRetries})`);
await new Promise((r) => setTimeout(r, delay));
return this.connect(retries + 1);
}
throw new Error(`WebTransport 連線失敗: ${err.message}`);
}
}
monitorClose() {
this.transport.closed.then(({ closeCode, reason }) => {
console.warn(`連線關閉: code=${closeCode}, reason=${reason}`);
this.connect();
});
}
}
最佳實踐總結
- 按場景選模式:即時音視訊/遊戲用資料報,聊天/檔案用雙向流
- 優先級分層:控制指令高優先級,媒體資料低優先級
- 優雅降級:偵測 WebTransport 可用性,不可用時回退 WebSocket
- 連線遷移利用:行動端場景下 QUIC 連線遷移避免重連中斷
- 流量控制:大檔案傳輸時注意流的背壓,避免記憶體溢位
本站提供瀏覽器本地工具,免註冊即可試用 →
#WebTransport#HTTP/3#QUIC#实时通信#流式传输