# WebRTC Connection Tester

> 在瀏覽器中診斷 WebRTC：白話說明的 ICE candidate 收集、NAT 類型偵測、UDP 阻擋檢查、TURN 伺服器驗證，以及跨兩個瀏覽器的真實 P2P 連線測試。

- URL: https://bytescope.dev/zh-Hant/webrtc-test/
- Locale: zh-Hant · Also: [en](https://bytescope.dev/en/webrtc-test.md) · [ja](https://bytescope.dev/ja/webrtc-test.md)
- Runs: 檔案不會離開你的瀏覽器 — 全部在本機處理。
- Requires: Any modern browser (Chrome, Edge, Firefox, Safari), desktop and mobile.
- Updated: 2026-09-08

## What it does

本機診斷在你開啟頁面的那一刻就開始執行：從多個公開 STUN 伺服器收集 ICE candidate 並分類 — host、server-reflexive、relay — 每一種都附一行說明它出現代表什麼意思。透過比對兩個不同 STUN 伺服器對應出的公開連接埠，工具會偵測你的 NAT 類型：cone NAT 對 P2P 友善，而 symmetric NAT 則代表直接連線經常會失敗、需要 TURN — 這是 WebRTC 除錯中最有用的一句話，直接講白。若完全沒有 server-reflexive candidate，代表你的網路封鎖了 UDP（在企業網路很常見），診斷結果會這樣告訴你。接著 loopback 測試會在頁面內真的連起兩個 peer connection，量測真實的 DataChannel 吞吐量與延遲。

TURN 驗證器是 WebRTC 開發者一直在找的功能：輸入你的 turn:／turns: URL 與憑證，工具會強制只走 relay 的連線，來證明你的 TURN 伺服器是否真的能配置（allocate） — UDP、TCP 與 TLS 傳輸方式分別測試，失敗原因也會診斷出來（憑證錯誤、防火牆、還是 DNS）。強制走 relay 的吞吐量測試會量測你的 TURN 伺服器實際能提供多少頻寬 — 這個數字決定了轉送的視訊畫質好不好，卻幾乎沒有工具會去量。

兩個瀏覽器一旦連上，它的用途就不只是診斷：你可以透過加密的 DataChannel 把任何大小的檔案，從一個瀏覽器直接傳到另一個 — 分塊傳輸並帶背壓控制，兩端都有即時進度與速度，你的資料從頭到尾不會經過任何伺服器（我們的或任何人的）。此外，連線用的文字區塊除了複製貼上，也可以顯示成 QR code 在裝置間交換 — 較長的 offer 會拆成自動輪播的短序列 — 在支援 BarcodeDetector API 的瀏覽器上，還能用內建的相機掃描器直接讀取。

一切都在你的瀏覽器中執行。連線測試只會和你選擇的 STUN／TURN 伺服器通訊；關於你網路的任何資訊都不會送到我們這裡。

## Limits

Does not diagnose a specific video-call app's servers; it tests STUN/TURN reachability and the peer connection this browser can make.

## Machine output

Copy JSON copies an envelope `{ tool, url, generatedAt, userAgentHint, result }`. `tool` is `"webrtc-test"`; `result` is the ICE / loopback / TURN snapshot below. Candidate addresses, URLs, usernames, and credentials are omitted.

- `mode` (string | null) — `"local"` | `"turn"` | `"p2p"`. `null` = mode not set.
- `rtcPeerConnectionAvailable` (boolean | null) — `null` = not probed; `false` = probed, `RTCPeerConnection` missing; `true` = constructor exists.
- `iceGatheringOutcome` (string) — `"not-run"` | `"running"` | `"timeout"` | `"complete"`.
- `iceGatheringMs` (number | null) — Max gathering duration in ms. `null` = gathering not finished and not running, or there was no candidate source to measure.
- `hostCandidateCount` (number | null) — Count of host candidates. `null` = gathering not finished or no candidate source (could not measure).
- `srflxCandidateCount` (number | null) — Count of srflx candidates. `null` = gathering not finished or no candidate source.
- `prflxCandidateCount` (number | null) — Count of prflx candidates. `null` = gathering not finished or no candidate source.
- `relayCandidateCount` (number | null) — Count of relay candidates. `null` = gathering not finished or no candidate source.
- `udpCandidateCount` (number | null) — Count of UDP candidates. `null` = gathering not finished or no candidate source.
- `tcpCandidateCount` (number | null) — Count of TCP candidates. `null` = gathering not finished or no candidate source.
- `tcpTypes` (string[]) — Distinct `tcptype` tokens, sorted. Empty when none observed or gathering was not counted.
- `ipv4CandidateCount` (number | null) — Count of IPv4 candidates. `null` = gathering not finished or no candidate source.
- `ipv6CandidateCount` (number | null) — Count of IPv6 candidates. `null` = gathering not finished or no candidate source.
- `mdnsCandidateCount` (number | null) — Count of mDNS candidates. `null` = gathering not finished or no candidate source.
- `gotSrflx` (boolean | null) — `null` = gathering not finished; `true` = at least one srflx candidate; `false` = finished, none.
- `gotRelay` (boolean | null) — `null` = gathering not finished; `true` = at least one relay candidate; `false` = finished, none.
- `defaultStunServerCount` (number) — How many built-in STUN servers this run used. 0 in turn mode.
- `customIceServerCount` (number) — 1 when a custom TURN URL was configured, else 0. The URL itself is omitted.
- `usingDefaultStunServers` (boolean) — `false` in turn mode; `true` otherwise.
- `natKind` (string | null) — `"cone"` | `"symmetric"` | `"indeterminate"` | `"udp-blocked"`. `null` = NAT not judged (not run, or not local mode).
- `loopbackOutcome` (string) — `"not-run"` | `"running"` | `"timeout"` | `"failed"` | `"ok"`.
- `loopbackSetupMs` (number | null) — Loopback setup duration in ms. `null` = not measured.
- `loopbackPingMedianMs` (number | null) — Median loopback ping in ms. `null` = not measured.
- `loopbackThroughputAvgMbps` (number | null) — Average loopback throughput in Mbps. `null` = not measured.
- `iceErrorCode` (number | null) — First numeric ICE/TURN error code. `null` = none observed.
- `selectedPairLocalType` (string | null) — Selected ICE pair local type: `"host"` | `"srflx"` | `"prflx"` | `"relay"`. `null` = not observed.
- `selectedPairRemoteType` (string | null) — Selected ICE pair remote type, same tokens. `null` = not observed.
- `iceConnectionRoute` (string | null) — `"direct"` | `"relayed"` | `"unknown"`. `null` = route not judged.
- `turnTransports` (array of objects) — Per-transport TURN probes. Empty outside turn mode. Each item: `transport` (variant label such as udp/tcp/tls, never a URL), `gotRelay` (boolean or `null` when that variant did not report), `gatheringMs` (number or `null` when not measured), `gatheringOutcome` (`"not-run"` | `"running"` | `"timeout"` | `"complete"`), `iceErrorCode` (number or `null` when that variant had no numeric error).

## FAQ

### host／srflx／relay candidate 分別代表什麼？

它們是 WebRTC 可以嘗試的位址：host = 你本機介面的位址（在現代瀏覽器中會以 mDNS 遮蔽）、srflx = STUN 伺服器看到的你的公開位址（它出現代表你的 NAT 可以被穿透）、relay = TURN 伺服器上的一個位址，會在直接路徑失敗時轉送流量。健康的設定會顯示 host + srflx；relay 只有在你設定了 TURN 時才會出現。

### 什麼是 symmetric NAT？它為什麼會讓 P2P 失敗？

symmetric NAT 會為你連往的每一個目的地都分配一個不同的公開連接埠，所以 STUN 伺服器觀察到的那個埠對真正的對端來說沒有用 — 打洞會失敗。工具透過比對來自兩個 STUN 伺服器的對應埠來偵測這點：連接埠不同 → symmetric。兩個都在 symmetric NAT 後方的對端，基本上一定需要 TURN 轉送。

### 為什麼我的 TURN 伺服器沒有回傳任何 relay candidate？

驗證器會分辨出各種原因：401／憑證失敗（帳密錯誤，或有時限的憑證已過期）、配置失敗（伺服器無法連到 — 防火牆擋了連接埠，或連接埠／傳輸方式錯誤），或主機名稱的 DNS 失敗。請分別測試 UDP、TCP 與 TLS — 企業網路常常只允許 443 埠上的 turns:。

### 這是在測我的網路連線速度嗎？

不是 — 頁面也誠實說明了這點。這些吞吐量數字量測的是 WebRTC 路徑：你機器的 WebRTC 堆疊（loopback）、你 TURN 伺服器的容量（強制 relay），或某一條特定的點對點連結。一般的下載／上傳測速需要專用的測試伺服器，這是一個無伺服器網站無法誠實提供的。

### 跨兩個瀏覽器的測試是做什麼的？

它會在兩台裝置之間建立一條真正的 WebRTC 連線，而且不需要任何 signaling 伺服器：一方把 offer 建立成一段壓縮過的文字，你用任何通訊軟體傳給對方，對方貼回一段 answer，連線就建立起來 — 接著即時統計會顯示你們是直連還是走 relay、來回時間、位元率與封包遺失率。它既是 WebRTC signaling 運作方式的示範，也是兩個真實網路之間貨真價實的連通性測試。

### 檔案直傳真的私密嗎？檔案可以多大？

檔案透過 WebRTC 加密的 DataChannel（DTLS）從一個瀏覽器直接送到另一個 — 沒有上傳這個步驟，也不存在會外洩或過期的伺服器副本；直連時，你的資料完全不會離開兩台機器之間的路徑（走 relay 的連線會經過你的 TURN 伺服器，但仍然是加密的）。檔案以 64 KB 分塊、帶背壓控制傳送，所以大小的上限取決於接收端瀏覽器的記憶體（檔案在那裡組裝完才存檔），而不是傳輸本身 — 幾百 MB 是家常便飯。

### QR code 交換是怎麼運作的？為什麼會有好幾格？

壓縮後的 offer 區塊大約 1,900 個字元 — 技術上塞得進一張 QR code，但那麼密的碼從螢幕上掃描很容易失敗。所以較長的區塊會顯示成一小串比較好掃的碼，自動輪播並標示第幾格；掃描器不管順序、收齊所有格後就會重組出原始區塊。讀取使用瀏覽器原生的 BarcodeDetector API（Chrome 與 Edge） — 不支援的瀏覽器可以改用手機的相機 App 掃描，或退回永遠可用的複製貼上。
