图 1:本文封面,窗口除以 RTT 的推导与代入结果。
先说明属性:这是一篇笔试真题复盘,题目出自本地历史笔试资料,与任何公司的当前招聘无关。题面很短:TCP 接收窗口 65535 字节、往返时延 RTT 0.1 秒,问这条连接的最大吞吐量是多少。它考的不是背数字,而是能不能从「窗口」和「RTT」两个概念里把吞吐上限推出来。
先建模型:稳态下每个 RTT 最多传一个窗口。
TCP 是可靠传输:发出去的数据要等对端 ACK 确认,任何时刻未确认的在途数据都不能超过接收窗口。稳态下发送方的节奏是——把一个窗口的数据连续发出去,然后停下来等 ACK,ACK 一到立刻补发下一段。一来一回,正好一个 RTT。于是每个 RTT 周期里最多传出一个窗口的字节,吞吐量天花板就是「窗口 ÷ RTT」。直觉上也好理解:窗口决定管道里同时能塞多少字节,RTT 决定多久确认一轮、腾出多少空间。
代入计算:65535 除以 0.1。
65535 ÷ 0.1 = 655350 字节每秒。选项如果按比特给,乘以 8 得 5242800 bit/s,约等于 5.24 Mbit/s。这里有两个容易错的点:一是单位,字节每秒和比特每秒差 8 倍,选项里往往同时摆着 5.24 和 0.66 两个数钓你;二是「最大」两个字——窗口没装满、RTT 抖动、丢包重传,实际吞吐只会比这个上限低。
为什么窗口偏偏封顶 65535:16 位字段与窗口扩大因子。
65535 不是随手给的数。TCP 首部里的窗口字段只有 16 位(RFC 9293 的首部格式写得明白),无符号最大值就是 2 的 16 次方减 1。16 位在低速链路时代够用,放到千兆、万兆链路上就成了一道硬顶。RFC 7323 为此定义了窗口扩大(Window Scale)选项:三次握手时双方协商一个移位数 n,之后报文里的窗口值一律按左移 n 位解释,相当于给窗口乘上 2 的 n 次方;n 最大为 14,窗口最多撑到约 1 GB。注意它只在握手阶段协商,连接建立后不能改。
代码验算:把推导和边界一起跑出来。
用 Node.js 22.15.0 跑一个计算器(node tcp-throughput.js):
// 题面:窗口 65535 字节,RTT 0.1 秒
function maxThroughputBytesPerSec(windowBytes, rttSec) {
return windowBytes / rttSec;
}
const win = 65535, rtt = 0.1;
const bps = maxThroughputBytesPerSec(win, rtt);
console.log(`窗口 ÷ RTT = ${win} ÷ ${rtt} = ${bps} 字节/秒`);
console.log(`换算比特: ×8 = ${bps * 8} bit/s ≈ ${(bps * 8 / 1e6).toFixed(2)} Mbit/s`);
// 窗口扩大因子:窗口值左移 n 位(RFC 7323 规定 n 最大 14)
for (const n of [0, 7, 14]) {
const scaled = win * 2 ** n;
console.log(`shift=${n}: 最大窗口 ${scaled} 字节 ≈ ${(scaled / 2 ** 30).toFixed(2)} GiB,`
+ `RTT 0.1s 时吞吐 ≈ ${(scaled / rtt * 8 / 1e9).toFixed(2)} Gbit/s`);
}
// 长肥网络:带宽时延积 BDP = 带宽 × RTT
const bdp = 1e9 / 8 * 0.1; // 1 Gbit/s 链路、RTT 0.1s
console.log(`1 Gbit/s × 0.1 s 的 BDP = ${bdp} 字节,`
+ `65535 字节窗口只装满 ${(win / bdp * 100).toFixed(2)}% 的管道`);
实测输出:
窗口 ÷ RTT = 65535 ÷ 0.1 = 655350 字节/秒
换算比特: ×8 = 5242800 bit/s ≈ 5.24 Mbit/s
shift=0: 最大窗口 65535 字节 ≈ 0.00 GiB,RTT 0.1s 时吞吐 ≈ 0.01 Gbit/s
shift=7: 最大窗口 8388480 字节 ≈ 0.01 GiB,RTT 0.1s 时吞吐 ≈ 0.67 Gbit/s
shift=14: 最大窗口 1073725440 字节 ≈ 1.00 GiB,RTT 0.1s 时吞吐 ≈ 85.90 Gbit/s
1 Gbit/s × 0.1 s 的 BDP = 12500000 字节,65535 字节窗口只装满 0.52% 的管道
最后一行是「长肥网络」(带宽大、RTT 长)的经典结论:1 Gbit/s 带宽配 0.1 秒 RTT,带宽时延积是 1250 万字节,65535 字节的窗口连管道的百分之一都填不满——不扩窗口,链路再宽也跑不出速度。
图 2:稳态时间轴、16 位上限的由来与长肥网络对策。
工程里的对策。
单连接被窗口卡住时,有四条路:开 Window Scale 撑大窗口,前提是两端都支持且接收缓冲够大;多条连接并行,把单连接的天花板乘以连接数;就近接入或走代理压低 RTT,公式的分母小了,吞吐自然上去;同时记住窗口只是上限之一,拥塞控制和接收方缓冲跟不上,照样白搭。跨地域调用大模型 API 的场景就是典型:长肥网络下单连接的吞吐天花板一眼能算出来,该不该上并发连接,先算这个数再决定。
回到笔试现场,建议的答题结构:先一句话给出公式「吞吐上限 = 窗口 ÷ RTT」,说清依据是稳态下每个 RTT 传一窗;再代入算出 655350 字节每秒、约 5.24 Mbit/s,注意区分字节与比特;最后补 16 位字段的来历与窗口扩大因子。算对是及格线,讲出 65535 的由来和 Window Scale 才是加分项。你排查过「带宽够却跑不满」的传输问题吗?最后卡在窗口、RTT 还是拥塞控制?欢迎说说。