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();
    });
  }
}

最佳實踐總結

  1. 按場景選模式:即時音視訊/遊戲用資料報,聊天/檔案用雙向流
  2. 優先級分層:控制指令高優先級,媒體資料低優先級
  3. 優雅降級:偵測 WebTransport 可用性,不可用時回退 WebSocket
  4. 連線遷移利用:行動端場景下 QUIC 連線遷移避免重連中斷
  5. 流量控制:大檔案傳輸時注意流的背壓,避免記憶體溢位

本站提供瀏覽器本地工具,免註冊即可試用 →

#WebTransport#HTTP/3#QUIC#实时通信#流式传输