模块二 · 运输层
公平性与传输层演化
前面把 TCP 的机制一个个拆开了,但还有两个问题没回答:当很多条 TCP 连接挤在同一条链路上时会怎样(公平性),以及为什么给传输层加新功能这么难(演化)。后一个问题尤其现实 —— 本机就有好几处"标准早就定了、但实际用不上"的例子。
本章要解决的问题:前面把 TCP 的机制一个个拆开了,但还有两个问题没回答:当很多条 TCP 连接挤在同一条链路上时会怎样(公平性),以及为什么给传输层加新功能这么难(演化)。后一个问题尤其现实 —— 本机就有好几处"标准早就定了、但实际用不上"的例子。
本章的证据来源:概念讲法参考教材§3.7–3.8,纸书173–186页(PDF192–205页),具体出处见章末。旧稿的命令与数值属于历史转述,相关附件未随当前项目提供,本次没有重新采集或运行原实验;模型计算、协议规则与观察不能互相替代。
先从一个例子理解概念
两个人同时下载,经过同一条有限容量的出口。怎样分配这份容量?先明确讨论的是每条流、每个用户还是每类业务,再谈公平。
共享瓶颈与公平
共享瓶颈是多股流量共同竞争的受限资源。公平必须说明度量和对象,连接数相同、用户份额相同和业务优先级相同是不同要求。
协议演化与中间设备
协议演化是修改或扩展通信机制;路径上的设备若按旧格式检查、过滤或改写,可能影响部署。能否使用新机制要看两端与路径共同支持。
公平性:很多条连接抢一条链路,怎么分
先明确问题。假设一条链路的容量是 20 个单位,现在有两条 TCP 连接同时在用它。它们各能拿到多少?
理想答案是各拿一半。 但为什么能均分?AIMD 规则里并没有写"要公平"这一条 —— 公平性是那个控制律自然收敛出来的结果。
实测:从 1 : 50 的极不平衡起点出发
模拟设置:瓶颈容量 20,两个流的窗口起点分别是 1 和 50(差 50 倍)。规则就是《拥塞控制》那一章讲的 AIMD:空闲时每个流窗口加 1,拥塞时每个流窗口减半。
第 1 轮 总需求 51 拥塞 流A=1 流B=25
第 3 轮 总需求 13 空闲 流A=2 流B=13
第 8 轮 总需求 10 空闲 流A=3 流B=9
第 16 轮 总需求 13 空闲 流A=6 流B=9
第 25 轮 总需求 18 空闲 流A=9 流B=11
第 35 轮 总需求 12 空闲 流A=7 流B=7
第 40 轮 总需求 22 拥塞 流A=5 流B=5终点:两个流都是 5,倍数差 1.00。 起点差 50 倍,40 轮之后完全追平。
为什么会自动追平? 关键在两条规则的不对称性:
| 规则 | 对落后流的效果 | 对领先流的效果 |
|---|---|---|
| 加性增(每次 +1) | 从 1 加到 2 —— 翻了一倍 | 从 50 加到 51 —— 只多 2% |
| 乘性减(每次减半) | 从 1 变成 1(已经很小) | 从 50 变成 25 —— 砍掉一半 |
落后的那个增长得更快(相对而言),领先的那个被砍得更狠。 一追一退,差距自然缩小。
一个容易被忽略的推论:AIMD 的分配是「围绕均分震荡」而不是稳定在均分
看上面那个表 —— 两个流的数值一直在变(11/9、7/7、5/5、6/9……),它们是在均分附近来回摆动,而不是稳定停在那儿。这是 AIMD 的固有特性:它总是"加到拥塞、退一半、再加",所以永远在动。
这对实际应用是有影响的:一条 TCP 连接的瞬时速率本来就是波动的。所以视频类应用才需要缓冲,也才会用自适应码率(《P2P 与内容分发网》那一章讲的 DASH)。
实测:插进一个"不退缩"的流
TCP 的公平性有个前提:所有参与者都遵守同一套规则。 如果有一个流见到拥塞也不退让,情况立刻变了。
模拟设置:容量仍是 20,两条正常的 TCP 流,外加一条以固定速率 12 传输、且不参与退让的流(这个行为类似不做拥塞控制的 UDP 应用)。
第 1 轮 总需求 32 拥塞 TCP-1=5 TCP-2=5 UDP样=12
第 2 轮 总需求 22 拥塞 TCP-1=2 TCP-2=2 UDP样=12
第 4 轮 总需求 18 空闲 TCP-1=4 TCP-2=4 UDP样=12
第 8 轮 总需求 18 空闲 TCP-1=4 TCP-2=4 UDP样=12
(此后不再变化)结果:那个固定速率的流拿走了 12/20 = 60% 的容量,两条 TCP 流各自被压到 4。
对比第一组实验:没有竞争时,每条 TCP 流能拿到约 10;现在只剩 4。
实测:并行连接能绕过公平性
第二个可能被绕过的假设是"公平是应用之间的公平"。实际上:
模拟设置:应用甲开 2 条连接,应用乙只开 1 条,三个流起点相同。
第 1 轮 总需求 15 空闲 甲-①=6 甲-②=6 乙-①=6
第 8 轮 总需求 21 拥塞 甲-①=3 甲-②=3 乙-①=3
第 16 轮 总需求 15 空闲 甲-①=6 甲-②=6 乙-①=6
第 40 轮 总需求 12 空闲 甲-①=5 甲-②=5 乙-①=5终点:应用甲合计 10,应用乙 5 —— 恰好 2 : 1。
应用甲并没有"更努力",它只是开了两条连接,就拿到了两倍的带宽。
还有一个不公平:RTT 偏差
第三类不公平和起点、规则都无关,而是物理条件造成的。
回顾 AIMD 的加性增:每经过一个往返时间,窗口加 1。
RTT = 10 ms 的连接:每秒能加 100 次
RTT = 200 ms 的连接:每秒只能加 5 次RTT 短的连接增长速度快 20 倍。 于是同样的时间内,近距离的连接窗口涨得远、抢到的带宽更多。这叫 RTT 偏差。
这正好解释了 CUBIC 为什么用"时间"而不是"RTT 轮次"做增长曲线的自变量(《拥塞控制》那一章实测本机用的是 CUBIC)。经典算法(Reno)的增长与 RTT 强相关,CUBIC 把它解耦了,让不同距离的连接更接近公平。
演化:为什么传输层很难加新功能
现在说第二个问题,它比公平性更现实。
标准的制定速度远快于部署速度。 传输层尤其明显,原因是:路径上有大量中间设备会检查甚至改写 TCP 报文头。
什么是"协议僵化"
你 ── 家用路由器 ── 运营商设备 ── 防火墙 ── 代理 ── 服务器
↑ ↑ ↑ ↑
都可能在检查/改写 TCP 头一个新设计的 TCP 选项,可能遇到三种命运:
| 中间设备的行为 | 结果 |
|---|---|
| 不认识这个选项,直接丢弃 | 新功能静默失效 |
| 不认识但保留(按标准应该这么做) | 新功能能用 ✓ |
| 不认识却按老规则改写 | 更糟:行为变得不可预测 |
最麻烦的是第三种 —— 协议设计者没法预测中间设备会做什么,于是倾向于不再改动 TCP 头。这种"因为改不动所以不改"的状态就叫协议僵化。
实测:本机就有好几处「标准早就定了,但实际用不上」
这部分是本章最实在的证据。下面每一条都有本机实测支撑:
| 功能 | 标准状态 | 本机实际情况 | 证据在哪一章 |
|---|---|---|---|
| HTTP/2 | 2015 年成为标准 | 本机 curl 8.21 的构建不含它(curl --http2 直接报不支持) | 《HTTP/2 与 HTTP/3》 |
| IPv6 | 1998 年成为标准 | 协议栈装了,但网络不提供 —— ping -6 返回 transmit failed | 《IPv6》 |
| ECN | 2001 年成为标准 | Windows 默认 disabled | 《拥塞控制》 |
| 发送配速 | — | 本机 Pacing Profile : off | 《拥塞控制》 |
| ICMP 差错报告 | 1981 年的标准 | 代理截获 ICMP,导致 tracert 结果全部失效 | 《ICMP》 |
| 路径 MTU 发现 | RFC 1191 | 依赖上一条的 ICMP,因此在本机失效 | 《段、包、帧、比特》 |
注意这张表里的年份:IPv6 是 1998 年、ECN 是 2001 年、HTTP/2 是 2015 年。二十多年过去,它们在一台普通的 Windows 机器上仍然用不上。
QUIC 是怎么绕开这个僵局的
理解了僵化,就能理解 QUIC 的设计选择为什么是那样。
问题:想让传输层更好(更快的建连、没有队头阻塞、连接迁移),但改 TCP 头会被中间设备搞坏。
QUIC 的答案:不改 TCP,而是把整套可靠传输搬到 UDP 之上,自己实现一遍。
| 选择 | 为什么 |
|---|---|
| 基于 UDP | 中间设备对 UDP 的干预少得多 —— UDP 没有"选项"这种机制,中间设备无从改写 |
| 强制加密(连握手都加密) | 中间设备看不到头部内容,也就改不了 |
| 在协议内实现流 | 不受 TCP 那种"一条连接一个字节流"的限制 |
| 用连接 ID 而不是四元组 | 换网络(Wi-Fi 切 4G)连接不断 |
| 自己实现拥塞控制 | 否则就变成上面实验里那个"不退缩的流",破坏公平性 |
代价也很明确:QUIC 要在用户态重新实现一遍序号、确认、重传、拥塞控制、流量控制 —— 这正是《从零构造可靠传输协议》那一章讲的全部内容。同时,把加密做进传输层意味着每一条 UDP 连接都要走完整的密码学握手,CPU 开销比裸 TCP 高。
常见误解
误解一:「TCP 的设计目标里包含公平性」
没有。 公平性是 AIMD 那两条规则自然收敛出来的副产品,不是设计目标。而且本章实测证明:只要有一个流不遵守规则、或者应用多开几条连接、或者 RTT 差得远,公平性立刻被破坏。
误解二:「用 UDP 就一定更快」
不一定。 UDP 的优势是省掉了握手和退让,代价是没有可靠性也没有公平性。在有丢包的链路上,一个不做重传的 UDP 应用拿到的有效数据可能还不如 TCP —— 因为 TCP 会把丢掉的补回来,而 UDP 掉了就是掉了。
误解三:「标准定了就能用」
本机实测直接反驳。 IPv6 定了 20 多年,这台机器上 ping -6 还是直接失败;ECN 定了 20 多年,Windows 默认还是关的。"标准支持"和"实际可用"是两件事,中间隔着整条部署链。
误解四:「协议僵化是厂商懒惰」
不完全是。 中间设备之所以改写 TCP 头,很多时候是出于实际需求:NAT 要改地址和端口、防火墙要过滤、负载均衡要看端口。它们不是故意阻挠新协议,而是在做自己该做的事 —— 只是这些行为的叠加让协议头变成了"不能碰的东西"。
误解五:「QUIC 证明了 UDP 优于 TCP」
QUIC 选 UDP 是为了绕过僵化,不是为了性能上的优劣。 它在 UDP 之上重新实现了一遍 TCP 的可靠传输和拥塞控制 —— 如果 TCP 头可以自由演进,这些工作本来不必重复做。
本章小结
| 结论 | 类型 |
|---|---|
| 公平性不是设计目标,而是 AIMD 规则收敛出的副产品 | 推理 |
| 模拟实测:两流从 1 : 50 的起点出发,40 轮后收敛到 5 : 5 | 模拟实测 |
| 收敛机制:加性增对落后流是相对快增长,乘性减对领先流是成比例削减 | 推理 |
| AIMD 的分配是围绕均分震荡,不是稳定在均分 | 模拟实测 |
| 模拟实测:一个不退让的固定速率流(12/20)把两条 TCP 流各压到 4 | 模拟实测 |
| TCP 退让而 UDP 不退让,这是"UDP 不公平"的机制 | 标准 |
| QUIC 基于 UDP 却主动实现拥塞控制,正是因为不想成为那个不退让的流 | 标准 |
| 模拟实测:开 2 条连接的应用拿到约 2 倍带宽(10 : 5) | 模拟实测 |
| "TCP 公平"是连接之间公平,不是应用之间或用户之间公平 | 推理 |
| 浏览器限制同域名并发连接数(约 6 条)是自律措施 | 标准 |
| RTT 短的连接因加加快而占优,这叫 RTT 偏差;CUBIC 用时间解耦了它 | 标准 |
| 中间设备会检查/改写 TCP 头,导致传输层协议僵化 | 标准 |
| 实测:本机 curl 8.21 构建不含 HTTP/2 支持 | 实测 |
实测:本机 IPv6 有协议栈但网络不提供(ping -6 直接失败) | 实测 |
| 实测:本机 ECN 默认 disabled、发送配速 off | 实测 |
| 实测:ICMP 被代理截获,导致 tracert 与路径 MTU 发现失效 | 实测 |
| 上述功能的标准化年份分别是 1998、2001、2015 —— 落地远滞后于标准 | 标准 + 实测 |
| QUIC 选择 UDP 是为了绕过僵化,是工程妥协而非协议优劣 | 推理 |
自测题
7 题 · 先自己答,再展开对照
AIMD 规则里并没有写"要公平",那为什么两条连接最后能均分?
因为收敛性来自那两条规则的不对称,而不是来自某个"公平"的设计目标。
具体说:加性增对落后方是相对快增长(从 1 加到 2 是翻倍,从 50 加到 51 只多 2%),乘性减对领先方是成比例削减(50 减半是砍掉 25,1 减半还是 1)。一追一退,差距自然缩小。本机模拟:起点 1 : 50,40 轮后收敛到 5 : 5。为什么"加性增、乘性减"这两条规则合起来能缩小差距?分别说一下它们对落后方和领先方的作用。
对落后方:加性增让它的窗口相对增长更快(1 → 2 是翻倍),而且乘性减几乎伤不到它(已经很小)。对领先方:加性增对它是杯水车薪(50 → 51 只多 2%),而乘性减每次都砍掉一半。
两者叠加的结果是差距单调缩小。一个不做拥塞控制的 UDP 应用和几个 TCP 应用共享一条链路,会发生什么?
这个 UDP 应用会持续挤占 TCP 应用的带宽。 因为 TCP 遇到丢包会退让(窗口减半),UDP 不会 —— 所以容量被 UDP 占住之后,TCP 只能去分剩下的部分。
本机模拟:容量 20、UDP 样流固定占 12 时,两条 TCP 流各自从约 10 被压到 4。
这也正是 QUIC 虽然基于 UDP 却主动实现完整拥塞控制的原因 —— 它不想成为那个不退让的流。两个应用竞争带宽,一个开 2 条连接、一个开 1 条。假设 AIMD 生效,它们各拿多少?这说明了什么?
开 2 条连接的拿约 2/3,开 1 条的拿约 1/3(本机模拟实测是 10 : 5,正好 2 : 1)。
说明 AIMD 让「每一条连接」趋于均分,而不是「每一个应用」均分。 所以多开连接能"绕过"公平性 —— 这也是浏览器限制同域名并发连接数(约 6 条)的原因:不加限制的话一个页面就能吃掉大半带宽。为什么 RTT 短的 TCP 连接能拿到更多带宽?CUBIC 怎么处理这个问题?
因为加性增是「每个 RTT 加 1」。 RTT 10 ms 的连接每秒能加 100 次,RTT 200 ms 的每秒只能加 5 次 —— 增长速度差 20 倍,窗口涨得快的自然抢到更多带宽。这叫 RTT 偏差。
CUBIC 的处理办法是把增长曲线的自变量从"RTT 轮次"改成"时间",这样不同距离的连接恢复速度接近,公平性改善(本机实测 Windows 用的就是 CUBIC)。什么是"协议僵化"?为什么它主要是中间设备造成的?
指协议头"改不动"的状态:因为新设计的选项可能被路径上的中间设备丢弃或改写,协议设计者于是倾向于不再改动协议头,演化就停滞了。
主要是中间设备造成的,但这些设备大多不是故意阻挠:NAT 要改写地址和端口、防火墙要过滤、负载均衡要看端口 —— 都是在做自己该做的事,只是行为的叠加让 TCP 头变成了"不能碰的东西"。「IPv6 在 1998 年就成为标准了,但本机
ping -6直接失败」——这个事实说明了什么?说明「标准支持」和「实际可用」是两个完全不同的状态。 从标准发布到实际能用,中间隔着整条部署链:你的系统要支持、对方的系统要支持、路径上所有中间设备都不能捣乱、应用还要愿意用。任何一环断掉,功能就退化成旧行为。
本机同类例子还有:curl 不含 HTTP/2 支持、ECN 默认关闭、ICMP 被代理截获导致tracert失效。二十多年前定的标准至今用不上,这并不罕见。
参考资料与适用范围
- 《计算机网络:自顶向下方法(原书第8版)》§3.7–3.8,纸书173–186页(PDF192–205页):本章概念与机制的对应教学章节,引用时需保留模型与版本条件。
- 教材作者配套教学资源入口:网站目前显示的教材版本与用户提供的第8版可能不同,页码以本章列出的中文第8版为准。
修订核对日期:2026-10-13。本次补充概念入口与教材出处;旧稿的具体数值为历史观察,未重新实测。相关脚本和完整日志未随当前项目提供,不能仅凭转述断言实验已复核,也不据此断言数据造假。