Home
Explore Uedu
Student Console
Register as Member/Login
(2) In future presentations of the research findings, in addition to the course project website and public presentations, your real name and personal information will not appear in this research report. If you are interested in the research results, we can provide you with an executive summary after the study is completed.
問卷中心
Teacher Console
Course Setup
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

0

Local AI

--

CISOSE26 本地 AI UG26
政治大學 AQI 18 29°C

AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

協定層與 TCP/IP

協定層與 TCP/IP(進階):你的 1 Gbps 光纖為何只跑得到 200 Mbps

從頻寬延遲乘積、壅塞控制的數學,到 NAT 子網路運算與 HTTP/3 如何繞過隊頭阻塞——深入現代傳輸層的設計權衡。

為什麼你的 1 Gbps 光纖,下載卻只有 200 Mbps?

你升級了家裡的光纖,帳單上寫著 1 Gbps。可是從一台位於美國西岸的伺服器下載大檔案,速度卻卡在 200 Mbps 上不去,而從台灣本地的 CDN 下載卻能輕鬆跑滿。線路明明一樣寬,差別到底在哪?

入門篇告訴我們 TCP 提供「可靠、有序的位元組串流」,並用三次握手建立連線。但它沒回答這個切身的問題:在一條真實、會塞車、有延遲的網路上,TCP 究竟用多快的速度送資料?答案是一條每隔幾毫秒就重新計算一次的動態曲線。理解這條曲線,你就能解釋上面的怪現象,也能看懂為什麼整個業界要花三十年,把壅塞控制(congestion control)演算法翻新一遍又一遍。

這篇進階篇不再重述分層與握手。我們直接鑽進三個入門篇只點到為止的硬核主題:頻寬延遲乘積與壅塞控制的數學位址耗盡逼出來的 NAT 與子網路運算,以及讓 HTTP/3 拋棄 TCP 的隊頭阻塞(head-of-line blocking)

頻寬延遲乘積:管線裡能塞多少水

要理解上面的怪現象,先建立一個關鍵直覺:網路像一條水管。它的「容量」不是只看管子多粗(頻寬 bandwidth),還要看管子多長(延遲 latency)。這個乘積叫做頻寬延遲乘積(BDP, Bandwidth-Delay Product)

$$\text{BDP} = \text{頻寬} \times \text{RTT}$$

協定層與 TCP/IP進階概念示意圖

BDP 代表「為了讓管線時時刻刻都滿載,必須有多少資料同時在飛」。TCP 靠滑動視窗(sliding window) 控制在途未確認(in-flight)的資料量;如果視窗比 BDP 小,管線就會出現空檔,頻寬被白白浪費。

動手算一下

回到開頭的台北↔美西連線。假設往返時間 RTT = 160 ms,我們想跑滿 1 Gbps:

BDP = 1 Gbps × 160 ms
    = 1 × 10^9 bit/s × 0.16 s
    = 1.6 × 10^8 bit
    = 2 × 10^7 byte
    = 20 MB

也就是說,要填滿這條管線,TCP 必須隨時保持 20 MB 的資料在途。問題來了:TCP 標頭裡宣告接收視窗的欄位只有 16 位元,最大 65535 byte(64 KB)。算一下這個上限能撐多少吞吐量:

window_bytes = 65535          # 16-bit 視窗欄位上限
rtt_s = 0.160                 # 160 ms
throughput_bps = window_bytes * 8 / rtt_s
print(f"{throughput_bps / 1e6:.1f} Mbps")   # 3.3 Mbps

只有 3.3 Mbps!這正是 1980 年代原始 TCP 在跨洲連線上的天花板。解法是 TCP 的視窗縮放選項(Window Scale Option):在三次握手時協商一個位移量 $s$,把實際視窗放大為 $\text{window} \times 2^s$,最高可達 1 GB 等級,才足以餵飽現代長肥管線(long fat network)。

但即使視窗夠大,還有第二道關卡:封包遺失率。這就引出壅塞控制的核心數學。

壅塞控制的數學:從 Reno 到 CUBIC

入門篇提過 AIMD(加法增大、乘法減小)與慢啟動。這裡我們把它量化,看看「為什麼遺失率一高,吞吐量就崩」。

經典的 TCP Reno 在穩態下,壅塞視窗 cwnd 每個 RTT 線性加一,一遇到遺失就砍半。把這條鋸齒狀曲線取平均,可推導出著名的 Mathis 公式:吞吐量與遺失率 $p$ 的關係是

$$\text{Throughput} \approx \frac{\text{MSS}}{\text{RTT}} \cdot \frac{C}{\sqrt{p}}$$

其中 MSS 是最大區段大小,$C$ 是常數(約 1.22)。注意那個 $\frac{1}{\sqrt{p}}$——吞吐量與遺失率的平方根成反比。我們來看這有多殘酷:

import math

def reno_throughput_mbps(mss=1460, rtt_ms=160, loss=1e-4):
    rtt = rtt_ms / 1000
    C = 1.22
    bytes_per_s = (mss / rtt) * (C / math.sqrt(loss))
    return bytes_per_s * 8 / 1e6

for p in [1e-5, 1e-4, 1e-3, 1e-2]:
    print(f"遺失率 {p:.0e} → {reno_throughput_mbps(loss=p):6.1f} Mbps")
遺失率 1e-05 → 282.0 Mbps
遺失率 1e-04 →  89.2 Mbps
遺失率 1e-03 →  28.2 Mbps
遺失率 1e-02 →   8.9 Mbps

開頭的謎底揭曉:跨洲連線的長 RTT 配上哪怕只是萬分之一的遺失率,Reno 就把你壓在不到 90 Mbps。線路的「寬度」根本不是瓶頸,RTT 與遺失率的組合才是。本地 CDN 之所以快,正因為 RTT 只有幾毫秒,且路徑短、遺失率極低。

CUBIC:用立方曲線記住上次的高點

Reno 在長肥管線上探測頻寬太慢(線性加一,要好幾秒才爬回高點)。Linux 預設的 CUBIC 改用一條三次方曲線來調整視窗。設 $W_{max}$ 是上次發生遺失時的視窗、$t$ 是距離上次遺失的時間,視窗依下式成長:

$$W(t) = C(t - K)^3 + W_{max}, \quad K = \sqrt[3]{\frac{W_{max}(1-\beta)}{C}}$$

這條曲線的妙處在於它的形狀:剛縮窗後,曲線在 $W_{max}$ 附近平緩(小心翼翼地逼近上次的瓶頸),離 $W_{max}$ 越遠則越來越陡(大膽探測新頻寬)。

def cubic_window(t, w_max=1000, C=0.4, beta=0.7):
    K = (w_max * (1 - beta) / C) ** (1/3)
    return C * (t - K) ** 3 + w_max

for t in [0, 2, 4, 6, 8, 10]:
    print(f"t={t:2d}s  cwnd≈{cubic_window(t):7.1f} 個區段")

關鍵是 CUBIC 的成長與 RTT 無關(只看真實時間 $t$),所以長 RTT 連線不再吃虧,這是它取代 Reno 成為主流的原因之一。

BBR:不再把遺失當作壅塞訊號

Reno 與 CUBIC 都把「封包遺失」當作網路塞車的訊號。但這個假設在現代網路越來越不成立——無線網路會因為干擾而遺失(不是塞車),而過大的路由器緩衝區又會造成緩衝膨脹(bufferbloat):封包不丟,但全卡在佇列裡排隊,延遲飆高。

Google 的 BBR(Bottleneck Bandwidth and RTT) 換了一套哲學:直接量測瓶頸頻寬 $BtlBw$ 與最小往返時間 $RTprop$,然後把在途資料量精準控制在 $BDP = BtlBw \times RTprop$ 附近。它追求的是那個「管線剛好填滿、佇列剛好不排隊」的甜蜜點——這正是排隊理論裡 Kleinrock 早在 1979 年指出的最佳運作點。BBR 不等遺失發生才反應,因此在有損的無線與長肥網路上吞吐量往往大幅領先。

NAT 與子網路:43 億個位址不夠用的後果

入門篇說 IPv4 只有約 43 億個位址早已不夠。它沒說的是:我們是怎麼撐到今天還沒用完的。答案是兩項技術:CIDR 子網路切割NAT(Network Address Translation)

動手算一下:子網路切割

你拿到公司配發的網段 203.0.113.0/24,要切成 4 個部門、每部門獨立子網路。/24 代表前 24 位元是網路前綴,剩 8 位元(256 個位址)是主機空間。切成 4 份,需要再借 2 個位元當子網路編號(因為 $2^2 = 4$):

import ipaddress

net = ipaddress.ip_network("203.0.113.0/24")
for sub in net.subnets(prefixlen_diff=2):   # /24 → /26,多借 2 位元
    hosts = list(sub.hosts())
    print(f"{sub}  可用主機 {hosts[0]} ~ {hosts[-1]}  共 {len(hosts)} 台")
203.0.113.0/26    可用主機 203.0.113.1 ~ 203.0.113.62    共 62 台
203.0.113.64/26   可用主機 203.0.113.65 ~ 203.0.113.126  共 62 台
203.0.113.128/26  可用主機 203.0.113.129 ~ 203.0.113.190 共 62 台
203.0.113.192/26  可用主機 203.0.113.193 ~ 203.0.113.254 共 62 台

注意每個 /26 有 64 個位址,但只有 62 台可用——因為每個子網路的第一個位址(網路位址,如 .0)與最後一個位址(廣播位址,如 .63)被保留。可用主機數的通式是 $2^{(32-n)} - 2$,其中 $n$ 是前綴長度。這個「減 2」是 CIDR 運算最常被學生忽略的陷阱。

NAT:一個公網位址,背後藏一整個家

你家裡可能有十幾台裝置同時上網,但對外只有一個公網 IP。這靠的是路由器上的 NAT,更精確地說是 PAT(Port Address Translation,又稱 NAPT)。它的把戲是:改寫封包的來源位址與來源埠號(source port),並維護一張轉換表記住對應關係。

內網裝置 A  192.168.0.10:51000 ─┐
內網裝置 B  192.168.0.11:51000 ─┤  NAT 改寫
                                 ▼
       對外都變成  203.0.113.5:?????(不同埠號區分)

NAT 轉換表:
┌─────────────────────┬──────────────────────┬─────────────────┐
│ 內部 (IP:port)      │ 對外 (IP:port)       │ 目的地          │
├─────────────────────┼──────────────────────┼─────────────────┤
│ 192.168.0.10:51000  │ 203.0.113.5:60001    │ 93.184.216.34:443│
│ 192.168.0.11:51000  │ 203.0.113.5:60002    │ 93.184.216.34:443│
└─────────────────────┴──────────────────────┴─────────────────┘

回應封包回來時,NAT 查表把目的地址改回內網裝置。這招讓一個公網 IP 服務數萬個連線(埠號有 16 位元,約 6.5 萬個),是 IPv4 苟延殘喘至今的關鍵。

但 NAT 也打破了網際網路「端到端(end-to-end)」的原始設計:外部主機無法主動連入內網裝置(因為轉換表只在內網先發起連線時才建立),這對 P2P、線上遊戲、視訊通話造成困擾,催生了 STUN/TURN/ICE 這類「打洞(hole punching)」技術。IPv6 位址空間夠大($2^{128}$ 個),本意就是要徹底擺脫 NAT,回歸端到端——但現實是 NAT 還會陪我們很久。

隊頭阻塞:為什麼 HTTP/3 把 TCP 給換掉了

入門篇結尾提到 QUIC 與隊頭阻塞,這裡我們把它講透。

隊頭阻塞(head-of-line blocking, HoL blocking) 指的是:一列隊伍中,只要排頭那個人卡住,後面所有人都得乾等,即使他們本身早就準備好了。

TCP 的可靠有序保證,恰恰製造了這個問題。現代網頁會在單一 TCP 連線上同時傳輸幾十個檔案(HTTP/2 的多工 multiplexing)。但 TCP 在它眼中只是「一條位元組串流」,並不知道上層其實是多個獨立檔案。於是:

單一 TCP 連線傳三個檔案:  [檔A片段][檔B片段][檔C片段][檔A片段]...
                                       ↑ 這個封包遺失了

TCP 的反應:在 B 的這個片段重傳到齊前,
            「按序交付」規則逼迫 C 的片段也得在緩衝區裡乾等
            ── 即使 C 早就完整收到!

檔 C 明明跟檔 B 毫無關係,卻因為 TCP 的有序保證被牽連卡住。連線數越多、遺失率越高,這個懲罰越重。

QUIC 的解法是:把「串流(stream)」的概念下放到傳輸層自己管理。QUIC 跑在 UDP 之上,在內部維護多條彼此獨立的串流,各自有獨立的序號與重傳。某條串流掉了一個封包,只會阻塞那一條串流,其他串流照常交付。這就根除了傳輸層的隊頭阻塞。

QUIC 還順手解決了另外兩件事:

  1. 連線建立更快。傳統 HTTPS 要先 TCP 三次握手,再 TLS 握手,來回好幾趟。QUIC 把傳輸與加密握手合併,新連線最快 1-RTT,重訪甚至 0-RTT 就能開始傳資料。
  2. 連線遷移(connection migration)。TCP 連線由「四元組」(來源 IP、來源埠、目的 IP、目的埠) 定義,你從 Wi-Fi 切到行動網路、IP 一變,連線就斷了。QUIC 用一個獨立的連線 ID(Connection ID) 辨識連線,換網路也不中斷——這對手機使用者體驗是巨大的改善。

這就是為什麼 HTTP/3 直接建立在 QUIC 上,而 QUIC 又建立在 UDP 上。繞了一大圈,我們又回到那個輕量、不可靠的 UDP——只是這次,可靠性、有序性、加密與多工,全部由 QUIC 在使用者空間重新、而且更聰明地實作了一遍。

重點回顧

  • 頻寬延遲乘積(BDP) = 頻寬 × RTT 決定要多少在途資料才能填滿管線;長肥網路必須靠視窗縮放才不會被 64 KB 的舊上限卡死。
  • Reno 的吞吐量正比於 $\frac{1}{\sqrt{p}}$(Mathis 公式),所以高 RTT + 微小遺失率就能讓跨洲連線遠遠跑不滿線路寬度。
  • CUBIC 用三次方曲線、成長與 RTT 解耦,在長肥網路勝過 Reno;BBR 改以量測頻寬與最小 RTT 為訊號,避開緩衝膨脹與「把無線遺失誤判為壅塞」的陷阱。
  • CIDR 子網路每段可用主機數是 $2^{(32-n)}-2$(保留網路與廣播位址);NAT/PAT 靠改寫埠號讓一個公網 IP 服務上萬連線,代價是破壞端到端連通性。
  • 隊頭阻塞源自 TCP 的有序保證;QUIC/HTTP3 把多串流下放到 UDP 之上各自重傳,並支援 0-RTT 與連線遷移,從根本上繞過了它。

深入探討(研究所視角)

把上面的線索收束起來,會看到一個更深的張力:TCP 的設計把「可靠」「有序」「壅塞控制」三件事綁死在同一個位元組串流的抽象裡,而這個抽象正在被現代網路逐一拆解。

第一條拆解線是控制迴路的訊號選擇。Reno/CUBIC 是遺失型(loss-based),把佇列填滿、丟封包當作回饋訊號,本質上會誘發 bufferbloat;BBR 是模型型(model-based),主動建構瓶頸頻寬與傳播延遲的模型,逼近 Kleinrock 的最佳點。但兩者共存於同一網路時的公平性是個尚未完全解決的研究難題——BBR 早期版本在與 CUBIC 競爭時會搶走過多頻寬,BBRv2/v3 為此加入了對遺失與 ECN(Explicit Congestion Notification) 訊號的回應,試圖更「禮貌」。這牽涉到控制理論裡的穩定性與收斂性分析,也牽涉到賽局論裡的公平分配。

第二條拆解線是把可靠性從核心搬到使用者空間。TCP 寫在作業系統核心裡,演進速度受制於全球作業系統的更新週期——一個新演算法要普及可能要十年。QUIC 在使用者空間實作,應用程式自帶協定堆疊,讓 Google、Meta 這類業者能以週為單位部署實驗與迭代。代價是 CPU 開銷較高(少了核心與網卡的硬體卸載),以及中間設備(middlebox)對 UDP 的不友善——這又回頭推動了 UDP 卸載與 GSO/GRO 等核心優化。

第三條線最根本:端到端原則(end-to-end principle) 的侵蝕與重建。NAT、防火牆、透明代理這些「中間設備」違背了「網路只負責傳遞、智慧放在端點」的原始信條,造成協定僵化(protocol ossification)——中間設備會偷看甚至改寫 TCP 標頭,導致任何新傳輸協定都難以上線。QUIC 的回應相當激進:把幾乎整個傳輸標頭都加密,讓中間設備什麼都看不到、無從干預,從而為傳輸層的未來演進「保留進化空間」。這是一個耐人尋味的轉折——為了讓協定能繼續演化,我們選擇對網路核心隱藏資訊。從 Saltzer、Reed 與 Clark 1984 年的端到端論文,到今天 QUIC 的加密傳輸層,這條主線串起了整部網路協定的演化史,也提醒我們:再穩固的基礎協定,都不是終點,而是下一輪設計權衡的起點。

AI 共讀助教正在陪你讀:協定層與 TCP/IP(進階):你的 1 Gbps 光纖為何只跑得到 200 Mbps
嗨!我是這篇文章的共讀助教,只根據〈協定層與 TCP/IP(進階):你的 1 Gbps 光纖為何只跑得到 200 Mbps〉的內容回答。可以問我「解釋某段」「舉個例子」「出題考我」,或反白文中段落後點下方「解釋選取段落」。