跳至主要內容
Skip to content

WebRTC 實戰 (4) —— ICE Candidate 與 NAT 穿越

如果你跟著前三篇實作,你可能會發現:在同一個 Wi-Fi 下連線沒問題,但一旦一台用 4G,一台用公司 Wi-Fi,連線就建立不起來。

這是因為我們還沒解決網路世界中最頑固的障礙:NAT (網路位址轉換)防火牆


一、 根本問題:為什麼 P2P 很難?

在現代網路中,大部分的設備都沒有公網 IP。你的設備可能處於一個私有網路中 (如 192.168.x.x)。當 A 想連線給 B 時,A 只知道 B 的私有 IP,但這在公網上是無法路由的。


二、 救援三劍客:STUN, TURN, ICE

1. STUN (Session Traversal Utilities for NAT)

功能:協助 ICE Agent 取得 Server Reflexive Candidate,反映 NAT 對外映射的位址與連接埠。媒體流量通常不經過 STUN 伺服器。

2. TURN (Traversal Using Relays around NAT)

功能:當直接路徑無法建立時,提供 Relay Candidate 中繼媒體流量。TURN 能顯著提高連通率,但仍可能受企業防火牆、傳輸協定、憑證或伺服器狀態影響,不能宣稱保證 100% 連通。

3. ICE (Interactive Connectivity Establishment)

功能:負責收集所有的連線可能性(稱為 ICE Candidates),並挑選出一條最優的路徑。


三、 前端實作:處理 ICE 事件

你需要在 RTCPeerConnection 建立後,立即監聽 onicecandidate 事件:

javascript
/* 前端代碼 */
const pc = new RTCPeerConnection({
  iceServers: [
    { urls: "stun:stun.l.google.com:19302" },
    { urls: "turn:your-turn-server.com", username: "u", credential: "p" },
  ],
});

pc.onicecandidate = (event) => {
  if (event.candidate) {
    signaling.send({
      type: "candidate",
      from: "User_A",
      to: "Target_User",
      payload: event.candidate.toJSON(),
    });
  }
};

const pendingCandidates = [];

signaling.onMessage = async (msg) => {
  if (msg.type === "candidate") {
    if (pc.remoteDescription) {
      await pc.addIceCandidate(msg.payload);
    } else {
      pendingCandidates.push(msg.payload);
    }
  }
};

async function flushPendingCandidates() {
  while (pendingCandidates.length > 0) {
    await pc.addIceCandidate(pendingCandidates.shift());
  }
}

ICE Candidate 有可能比 Offer/Answer 更早抵達。應先暫存 Candidate,等 setRemoteDescription() 成功後再呼叫 flushPendingCandidates(),避免 addIceCandidate() 因尚未存在遠端描述而失敗。

WARNING

範例中的 TURN 帳密只用於說明格式。正式環境不應把長期憑證寫死在前端,應由後端簽發短效 TURN 憑證,並提供 UDP、TCP 與 TLS 等可用路徑。


總結

今天我們解開了 WebRTC 連線最痛苦的一環:NAT 穿透與 ICE Candidate 交換邏輯。至此,你已經完整掌握了底層原理實戰的所有連線流程。

下一篇,我們將介紹一個常被忽略的強大武器:Data Channel。


進階挑戰

打開 Chrome 瀏覽器並輸入 chrome://webrtc-internals/。在進行 WebRTC 連線時觀察 IceCandidatePair 的狀態變遷。你能分辨出目前正在使用的連線路徑是 host (內網)、srflx (STUN/公網) 還是 relay (TURN/中繼) 嗎?


延伸閱讀與資源

← 上一章:SDP 交換與媒體協商 | 返回專題首頁 | 下一章:Data Channel