传输层:TCP 与 UDP
TCP 在不可靠的 IP 之上重建了三个承诺:可靠(不丢)、按序、流量控制——外加最重要的拥塞控制(对网络友好)。本章讲三次握手、滑动窗口、重传与拥塞控制,以及 QUIC 为什么把传输层搬进用户态(→ I/O 模型与事件驱动)。
UDP:极简
Section titled “UDP:极简”UDP 只加两件事:端口(多路分解)与校验和——不保证到达、不保证顺序、不重传。它的用武之地:延迟优先于完整性(实时音视频、游戏)、应用自己重建可靠性(QUIC)、以及广播/多播。
TCP 的三个承诺
Section titled “TCP 的三个承诺”序列号 + 确认(ACK):每个字节有编号,接收方确认收到的位置;超时重传(RTO 估计自 RTT)与快速重传(三个重复 ACK 即重传,不等超时)。按序由接收方重组缓冲保证。
滑动窗口:接收方通告剩余缓冲(rwnd),发送方不超过——防止快发送方淹没慢接收方(这是端系统的自我保护,与拥塞控制不同)。
连接:三次握手
Section titled “连接:三次握手”SYN → SYN-ACK → ACK,双方交换初始序列号(ISN)并确认——两次不安全(旧的重复 SYN 会建立幽灵连接),四次冗余。挥手(FIN)四次因为半关闭(一方还能发)。
拥塞控制防止发送方淹没网络——发送方探测可用带宽,丢包即减速。拥塞控制是互联网没有崩溃的原因:没有它,端系统会把路由器的缓冲打满、丢包率爆炸、 everyone 变慢(拥塞崩溃)。
经典算法(TCP Reno):
- 慢启动:窗口从 1 MSS 指数增长到阈值;
- 拥塞避免:线性增长(每 RTT +1);
- 丢包反应:超时(窗口归 1)或快速重传(减半)——AIMD(加性增、乘性减)。
AIMD 的哲学:用丢包作为拥塞信号——路由器缓冲满 = 丢包 = 减速。问题有二:
- 长肥管道(带宽 × RTT 大)上,从"丢包"到"知道"要一个 RTT,窗口震荡大——为什么 TCP 在长肥管道上慢;
- 缓冲膨胀(bufferbloat):大缓冲让丢包来得太晚,延迟先爆炸(排队延迟数百 ms)。
现代算法:
- Cubic(Linux 默认):用三次函数增长,丢包后快速恢复——对长肥管道友好;
- BBR(Google):以带宽与 RTT 建模而非丢包——直接估计 BDP(带宽×延迟),把发送速率控制在瓶颈附近,不依赖丢包信号,bufferbloat 网络上延迟低一个量级。
队头阻塞与 QUIC
Section titled “队头阻塞与 QUIC”队头阻塞(head-of-line blocking):TCP 保证字节流按序——一个包丢了,后面所有已到的字节都要等它重传,应用层看到的延迟是整个重传周期。HTTP/2 的多路复用(→ HTTP/1.1、HTTP/2 与 HTTP/3)把多个流塞进一条 TCP 连接,反而放大了队头阻塞:一条流的丢包卡住所有流。
QUIC 的对策:把传输层搬到用户态以便快速演进——UDP 之上重建可靠传输,每个流独立重传(流内按序、流间独立),消除队头阻塞。顺带:TLS 1.3 内建(握手与传输合并)、连接迁移(连接 ID 不绑定 IP,换网不断线)、0-RTT 恢复。HTTP/3 = HTTP over QUIC。
为什么放用户态:内核 TCP 改一次要十年(内核版本、部署面);用户态协议随应用发布——演进速度成为设计目标(与端到端原则一脉相承,→ 分层模型与 IP)。
- UDP 极简:延迟优先或自建可靠(QUIC)时用它
- TCP 三承诺:可靠(序列号+重传)、按序(重组)、流量控制(rwnd);三次握手安全在序列号交换
- 拥塞控制是互联网没有崩溃的原因;AIMD 用丢包信号,Cubic 适配长肥管道
- BBR 以带宽与 RTT 建模而非丢包,bufferbloat 上延迟低一个量级
- 队头阻塞:TCP 按序让一个丢包包住后面所有字节;QUIC 每流独立重传消除它
- QUIC 把传输层搬到用户态以便快速演进