← 返回题目列表

什么是带宽时延积(BDP)?TCP 窗口和缓冲区如何调优才能跑满带宽?

高频 困难 第 16 / 27 题 更新于 2026/08/01
BDP滑动窗口窗口缩放TCP调优

简化版

带宽时延积(BDP = 带宽 × 往返时延 RTT) 表示「这条链路在途中最多能装下多少数据」,是 TCP 想跑满带宽时发送窗口至少要有的大小。TCP 靠滑动窗口控制「一次能发多少还没被确认的数据」,窗口大小取 拥塞窗口(cwnd)和对端通告的接收窗口(rwnd)中较小者。核心结论:如果窗口 < BDP,管道填不满,带宽利用率上不去,尤其在高带宽 + 高延迟(长肥管道,LFN)时特别明显。调优要点:① 开启窗口缩放(Window Scaling) 突破 TCP 头 16 位窗口 64KB 的上限;② 把接收/发送缓冲区调到 ≥ BDP(缓冲区限制了窗口能开多大);③ 用更好的拥塞控制算法(如 BBR)。归结起来就是:窗口要 ≥ BDP,才能让数据把整条管道填满。

详细版

一、为什么发一点等一下会浪费带宽

TCP 不是发一个包等一个 ACK(那样太慢),而是允许连续发出一批「未确认」的数据,这批数据的上限就是发送窗口。窗口越大,一次能塞进链路的数据越多。

  • 如果窗口太小:发出去一点点就得停下等 ACK,链路大部分时间空着,带宽白白浪费
  • 理想情况:窗口大到「上一批数据的 ACK 刚回来时,管道正好一直是满的」,就能持续跑满带宽。

二、BDP:管道的容量

把链路想象成一根水管:带宽是水管粗细,RTT 是水管长度,那么这根管子里能容纳的水量就是:

BDP(字节) = 带宽(字节/秒) × RTT(秒)

例:带宽 1 Gbps、RTT 100ms:

BDP = 1,000,000,000 bit/s ÷ 8 × 0.1 s = 12,500,000 字节 ≈ 12.5 MB

要跑满这条链路,发送窗口至少要 ≈ 12.5MB。如果窗口只有 64KB,利用率只有 64KB / 12.5MB ≈ 0.5%——大部分带宽闲置。

三、窗口取谁:min(cwnd, rwnd)

实际发送窗口 = min(拥塞窗口 cwnd, 接收窗口 rwnd)

  • rwnd(接收窗口):对端在 TCP 头里通告「我还能收多少」,受对端接收缓冲区限制。
  • cwnd(拥塞窗口):本端根据网络拥塞情况自己估算的「网络能承受多少」,受拥塞控制算法控制。
  • 两者取小的——任何一个太小,都会拖累吞吐

四、三个调优抓手

  1. 窗口缩放(Window Scaling):TCP 头的窗口字段只有 16 位,最大 65535 字节(64KB),远小于现代 BDP。窗口缩放选项(握手时协商)给窗口加一个「放大倍数」,突破 64KB 上限。没有它,高 BDP 链路永远跑不满。
  2. 调大缓冲区:接收缓冲区决定 rwnd 上限,发送缓冲区决定能缓存多少待确认数据。缓冲区 < BDP,窗口就开不到 BDP。 现代系统多用「缓冲区自动调整」。
  3. 换拥塞控制算法:cwnd 由算法控制,传统 CUBIC 在丢包时激进降窗,高延迟链路恢复慢;BBR 直接估算带宽和 RTT,能更快、更稳地把窗口开到接近 BDP。

完整版教学

一、先理解「窗口」为什么决定吞吐

TCP 要可靠传输,就必须能重传丢失的数据,所以发出去但还没被确认的数据要留着。这批「已发送未确认」的数据量上限,就是发送窗口

关键公式(理想吞吐):

最大吞吐 ≈ 窗口大小 / RTT

因为「发出一窗口的数据 → 等一个 RTT 收到 ACK → 才能滑动窗口继续发」。所以:

  • RTT 固定时,窗口越大,吞吐越高
  • 窗口固定时,RTT 越大,吞吐越低(这也是跨国链路慢的原因之一)。

要让吞吐达到链路带宽,就需要 窗口 / RTT ≥ 带宽,即 窗口 ≥ 带宽 × RTT = BDP。这就是 BDP 的意义——它是「跑满带宽所需的最小窗口」

二、BDP 的直观图景:长肥管道(LFN)

把链路想成水管:

  • 带宽 = 管子的横截面积(越粗,单位时间流量越大)。
  • RTT = 管子的长度(数据来回一趟的路程)。
  • BDP = 面积 × 长度 = 管子的容积 = 这条链路「在飞的数据」上限。
带宽 1Gbps、RTT 100ms → BDP ≈ 12.5MB
带宽 100Mbps、RTT 10ms → BDP ≈ 125KB

又粗又长的管道(高带宽 + 高延迟)叫「长肥管道」(LFN, Long Fat Network),比如跨国千兆专线、卫星链路。它们 BDP 巨大,对窗口的要求最高,也最容易因为窗口不够而跑不满——这类链路是 TCP 调优的重灾区。

三、窗口的两个上限:rwnd 和 cwnd

发送方实际能用的窗口 = min(rwnd, cwnd),两个都要够大:

① 接收窗口 rwnd(流量控制,别淹死接收方)

对端在每个 ACK 的 TCP 头里通告 Window 字段:「我的接收缓冲区还剩这么多空间。」发送方不能发超过 rwnd 的未确认数据,否则会淹没接收方。rwnd 的上限由接收方的接收缓冲区大小决定。

② 拥塞窗口 cwnd(拥塞控制,别压垮网络)

发送方自己维护 cwnd,估计「网络当前能承受多少」。它从慢启动开始涨,遇到丢包就降,由拥塞控制算法(CUBIC、BBR 等)动态调整。

任何一个是短板,吞吐就被卡住:接收缓冲区小 → rwnd 小;网络拥塞或算法保守 → cwnd 小。

四、第一个坑:16 位窗口字段的 64KB 天花板

TCP 头里的 Window 字段只有 16 位,最大表示 65535 字节 ≈ 64KB。这是 1980 年代的设计,当时够用,现在远远不够:

  • 前面例子 BDP 12.5MB,而窗口最大 64KB → 利用率 0.5%。

解决:窗口缩放选项(Window Scale Option, RFC 1323)。在三次握手时双方协商一个「缩放因子」,实际窗口 = 通告值 × 2^缩放因子,最大可到约 1GB。

关键:窗口缩放只在握手时协商,中途不能改。如果一端不支持或被中间设备(防火墙)剥掉了这个选项,整条连接就退回 64KB 上限,高 BDP 链路直接残废。这是排查「大带宽却跑不满」时要重点看的点。

五、第二个坑:缓冲区太小,窗口开不大

窗口能开多大,受制于缓冲区大小

  • 接收缓冲区SO_RCVBUF / 系统 net.ipv4.tcp_rmem):限制 rwnd 上限。
  • 发送缓冲区SO_SNDBUF / net.ipv4.tcp_wmem):限制能缓存多少「已发未确认 + 待发」数据。

要跑满 BDP,两端缓冲区都得 ≥ BDP。 缓冲区设小了,即使开了窗口缩放,窗口也开不到 BDP。

现代 Linux 默认开启缓冲区自动调整tcp_moderate_rcvbuftcp_window_scaling),会根据链路情况动态放大缓冲区,多数场景不用手动调。但对超大 BDP 的长肥管道,可能需要手动调大 tcp_rmem/tcp_wmem 的上限。

副作用提醒:缓冲区不是越大越好。过大的缓冲区会导致 Bufferbloat(缓冲区膨胀)——数据在缓冲区里排长队,RTT 暴涨、延迟变差。要在吞吐和延迟间平衡。

六、第三个坑:拥塞控制算法保守

即使窗口和缓冲区都够大,cwnd 由拥塞控制算法说了算

  • CUBIC(Linux 默认):基于丢包判断拥塞,一丢包就大幅降 cwnd,再慢慢涨回来。在高延迟 + 偶发丢包(如跨国链路、无线)时,cwnd 反复被砍,很难稳定维持在 BDP 附近,吞吐上不去。
  • BBR(Google):不靠丢包,而是主动测量瓶颈带宽和最小 RTT,直接把发送速率控制在「带宽 × RTT ≈ BDP」附近。在长肥管道、有随机丢包的链路上,BBR 往往能显著提升吞吐。

所以长肥管道调优的完整拳法是:窗口缩放(破 64KB) + 大缓冲区(≥ BDP) + BBR(把 cwnd 稳定推到 BDP)

七、调优实战清单

排查/优化「大带宽却跑不满」时依次检查:

  1. 算 BDP:带宽 × RTT,心里有数需要多大窗口。
  2. 确认窗口缩放开启net.ipv4.tcp_window_scaling=1,且抓包确认握手时没被中间设备剥掉。
  3. 调大缓冲区上限tcp_rmem/tcp_wmem 的最大值要 ≥ BDP(或依赖自动调整)。
  4. 换拥塞控制net.ipv4.tcp_congestion_control=bbr(需要内核支持)。
  5. 注意 Bufferbloat:缓冲区别无脑拉爆,兼顾延迟。
  6. 多流并发兜底:单流受限时,多条 TCP 流并行(如多线程下载)也能提升总吞吐——很多下载工具就是这么干的。

八、常见误区与追问

考点正确口径
BDP带宽时延积,表示链路管道容量
窗口不足发送窗口小于 BDP 时无法跑满带宽
调优窗口扩大、缓冲区、拥塞控制、合理并发
BDP = bandwidth * RTT
1 Gbps, RTT 80 ms:
1e9 / 8 * 0.08 = 10 MB
send window should be near or above 10 MB to fill pipe

高带宽长 RTT 链路想跑满,窗口要能装下一个 BDP 的在途数据。

  • 误区:带宽高就一定吞吐高。 如果 RTT 高而窗口小,在途数据不足,链路会空转。
  • 误区:只调接收缓冲区就够。 吞吐还受拥塞窗口、发送缓冲区、应用读写速度和丢包影响。
  • 误区:窗口越大越安全。 过大缓冲可能造成排队延迟、内存占用和拥塞恢复变慢。
  • 追问:BDP 怎么估算? 用带宽乘 RTT,注意单位换算,结果就是链路上可容纳的数据量。
  • 追问:窗口扩大选项解决什么? TCP 原始窗口字段 16 位最大 65535 字节,窗口扩大支持更大窗口。
  • 追问:为什么跨国链路更容易受窗口影响? RTT 大导致 BDP 大,需要更多在途数据才能填满链路。

九、加强记忆

BDP = 带宽 × RTT,是「链路里能装下的数据量」,也是跑满带宽所需的最小发送窗口。TCP 发送窗口 = min(接收窗口 rwnd, 拥塞窗口 cwnd),吞吐 ≈ 窗口 / RTT,所以窗口 < BDP 就填不满管道,在高带宽 + 高延迟的「长肥管道」上尤其明显。调优三板斧:窗口缩放(突破 TCP 头 16 位的 64KB 上限)、缓冲区 ≥ BDP(否则窗口开不大,但别过大以免 Bufferbloat)、用 BBR(把 cwnd 稳定推到 BDP 附近)。核心一句:要跑满带宽,窗口就得撑满 BDP。