想象你开了一家小小的奶茶店,前台的出餐托盘一次只能放四杯。顾客来了,做好的奶茶一杯接一杯摆上托盘,托盘满了你就得停手,等顾客拿走几杯、腾出空位,才能继续补上新做好的。与此同时,顾客的“肚子容量”也是一个限制——他能喝多少,决定了你一次最多敢给他做几杯。而如果外卖小哥取餐的路上堵得水泄不通,你还得根据路况调整出餐节奏,免得奶茶送不出去堆在门口。这个“托盘 + 肚子 + 路况”的三方博弈,就是计算机网络里大名鼎鼎的滑动窗口机制。 滑动窗口是 TCP/IP 协议栈保证可靠、高效传输的核心机制。它让发送方不必每发一个包就傻等确认,而是可以一口气发送一批数据,像流水线一样持续工作。本篇文章会从基础原理讲起,带你认识发送窗口、接收窗口、累积确认、流量控制、拥塞控制,再到性能调优和故障排查。你会发现,理解了奶茶店的托盘逻辑,也就顺便理解了 TCP 最精妙的设计之一。

岗位关联

对 AI 从业者而言,滑动窗口是理解分布式 AI 集群通信、大模型训练数据管道、云端推理服务网络调优的基础——你调模型调得再快,网络瓶颈照样卡住整个系统的喉咙。

目录
  1. 可视化演示
  2. 1. 基础原理(托盘与流水线)
  3. 2. 确认机制(顾客的一次回头率)
  4. 3. 流量控制(顾客的食量告白)
  5. 4. 拥塞控制(路况决定车速)
  6. 5. 性能调优(管道里能装多少杯)
  7. 6. 故障排查(外卖丢了多久该重做)
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态展示TCP滑动窗口的数据发送与ACK确认过程:段状态机颜色区分已确认/已发送/窗口内/未发送,数据包双向流动展示。

本节配有交互动效(进入视口后自动加载,约 3KB)。

数据包与ACK双向流动 发送端发送数据包,接收端返回ACK确认 ACK确认已收到的连续字节,窗口向前滑动 Win = min(cwnd, rwnd) 拥塞窗口与接收窗口取较小值

1. 基础原理(托盘与流水线)

回到奶茶店。你面前这个四杯容量的出餐托盘,就是“发送窗口”的化身。托盘有多大,决定了你一次最多能摆几杯;顾客的肚子容量则是“接收窗口”,决定了他一次最多能消化几杯。而每一杯奶茶上的取餐号,就是网络包里的“序列号”——有了它,顾客才知道先喝哪杯、有没有漏掉的。发送方手里拿着一个“窗口”标识,窗口左边表示已确认的奶茶(顾客喝完了),窗口右边表示还没做的奶茶,窗口内部就是“已做好但还没被拿走”的区间。

当顾客拿走一杯并说“收到了”,你对应的取餐号就从窗口左侧“滑”出去,同时从右侧补进一个待做的新号。这个过程就叫“窗口滑动”——本质是发送方和接收方的序号边界不断前移。真正决定你能发多少的,不是托盘也不是肚子单独说了算,而是两者取小:如果托盘能放 10 杯但顾客只吃得下 4 杯,那你一次最多做 4 杯。

这套“双窗口取小”的设计,让发送方既不会把接收方撑爆,也不会把网络堵死。就像奶茶店既会看顾客脸色出餐,也会留意外卖小哥的送餐路线堵不堵——两边的信号都要听,才能细水长流地做生意。

Swnd=min⁡(Rwnd,  Cwnd)\displaystyle Swnd = \min (Rwnd, \; Cwnd)
发送窗口 Swnd 是“实际能发的数据量”,它同时受接收方通告的接收窗口 Rwnd 和网络拥塞窗口 Cwnd 的限制,任何一方变小都能立刻踩下刹车。
  1. 两个候选容量Rwnd 是接收方当前还能装下的字节数,Cwnd 是网络允许发送的字节数,两者各自独立、互不替代。
    Rwnd,  Cwnd\displaystyle Rwnd, \; Cwnd
  2. 取最小值运算min 表示“谁小听谁的”,发送方的实际可用量不会超过任何一个瓶颈,就像两根串联的水管,流量取决于最细的那根。
    min⁡(Rwnd,  Cwnd)\displaystyle \min (Rwnd, \; Cwnd)
  3. 分配结果Swnd 就是最终的发货上限,每个往返周期内发送方最多发出 Swnd 字节的数据,窗口再大也不会超出这个边界。
    Swnd\displaystyle Swnd

2. 确认机制(顾客的一次回头率)

奶茶店里,你不需要等顾客喝完一杯、喊一句“好喝”,再开始做下一杯——那样效率太低了。更聪明的做法是:顾客攒了几杯一起喝,然后回头喊一句“1 号到 4 号我都喝完了”,你就知道这四杯全部安全送达,可以接着做新号了。这就是“累积确认”:接收方不必逐个包回复,只告诉发送方“某个序号之前的所有数据都收到了”。

累积确认还有一个好处:就算中间某个确认丢了,只要后面来了一个更大的确认号,发送方也能推断之前的包都无恙。举个反例:如果顾客喊“1 到 4 号喝完了”这句话被风吹跑了,你惴惴不安,但紧接着顾客又喊“1 到 6 号都喝完了”,你立刻安心——5、6 号都到了,1 到 4 号自然没丢。这就是确认丢失时的自我修复。

如果数据正好要反向传输——比如顾客喝完还想再点一杯新奶茶——他就会在点单时顺带说一句“刚才的喝完了”。这一步叫“捎带确认”,把确认信息搭在反向数据上,白白省了一次单独的往返。正是这种“一带而过”的批量认可,让滑动窗口比古老的一问一答式协议快出好几倍。

ack=seqlast+1\displaystyle ack = seq_{\text{last}} + 1
ACK 报文中携带的确认号,表示“此序号之前的所有字节都收到,下一个期望收到的字节是 seq_last + 1”。
  1. 连续数据流数据被切成一段段有序的字节流,每一段都有唯一的序列号 seq,接收方按号拼接。
    seq\displaystyle seq
  2. 寻找最后连续片段seq_last 表示接收方收到的、从起始处最长的连续段的末尾序号,中间若有空洞,只能算到空洞之前。
    seqlast\displaystyle seq_{\text{last}}
  3. 加一得到期望值seq_last + 1 是接收方“下一个想要”的序列号,发送方看到这个 ack,就知道 seq_last 及以前全部作数。
    ack=seqlast+1\displaystyle ack = seq_{\text{last}} + 1

3. 流量控制(顾客的食量告白)

某天顾客说:“我今天胃不太舒服,你每批最多送 2 杯过来。”这句话翻译到网络里,就是接收方在 ACK 里附带一个“窗口通告”字段,通知发送方自己的接收缓冲区还剩多少空间。发送方看到通告后,会立刻把发送窗口收缩到对方能吃下的量。这就是流量控制——它管的是“接收方的肚子”,防止发得太快把对方缓冲区撑爆。

如果情况更糟,顾客说“我一口都喝不下了”,窗口通告变成 0,这就是“零窗口”状态。此时发送方必须彻底停手,绝不能硬塞。但万一顾客消化完、腾出空间了,他又怎么通知你?这儿有个陷阱:如果发送方傻等对方主动通知,而承载通知的 ACK 包在半路丢了,双方就会陷入死锁——一个等通知,一个以为对方不想喝。所以 TCP 引入了“持续定时器”:发送方每隔一段时间主动发一个 1 字节的探测包,试探“现在能喝了吗?”直到顾客回复“能了”,才恢复出餐。

流量控制的本质是“尊重对方的消化速度”。就像你不会往一个喊着吃饱了的顾客嘴里硬塞奶茶,TCP 也不会往一个通告零窗口的接收方拼命发包。正是这种体贴,让收发双方在内存有限的真实机器上也能默契配合。

Rwnd=RcvBuf−(LastRcvd−LastRead)\displaystyle Rwnd = RcvBuf - (LastRcvd - LastRead)
接收窗口 Rwnd 等于接收缓冲区总空间减去“已经收到但还没被应用层读走的数据量”,剩余空间才是发送方可以继续发的额度。
  1. 总缓冲区大小RcvBuf 是接收方为这条连接预留的内存总量,相当于顾客这一顿饭的总食量上限。
    RcvBuf\displaystyle RcvBuf
  2. 未读数据量LastRcvd 表示已收到的最高字节序号,LastRead 表示应用层已经读到的位置,两者之差就是“堆在缓冲区里还没消化”的数据。
    LastRcvd−LastRead\displaystyle LastRcvd - LastRead
  3. 相减得剩余空间用总容量减去未消化部分,剩下的就是还能接收的字节数,这个数会随每次 ACK 通告给发送方。
    Rwnd=RcvBuf−(LastRcvd−LastRead)\displaystyle Rwnd = RcvBuf - (LastRcvd - LastRead)

4. 拥塞控制(路况决定车速)

蛋糕店出餐快,但门外的小路却只有两车道。如果不管不顾地全速发货,奶茶全堵在路上,反而更慢。TCP 的拥塞控制就是发送方自己去“探路”的过程。刚开始,它像谨慎的新手司机,只发 1 个包试探水深——这叫“慢启动”。如果确认准时回来,说明路况不错,就翻倍加速:2 个、4 个、8 个……指数级试探。

当窗口涨到某个阈值时,发送方转入“拥塞避免”阶段,不再翻倍,而是每个往返只增加 1 个包,像老司机在高速上稳住油门匀速前进。一旦真的发生丢包——路况爆表了——发送方立刻把阈值砍半,进入“快速恢复”。这里有个聪明的优化叫“快速重传”:接收方如果发现中间少了个包,会连发三次重复 ACK 大喊“5 号呢?”发送方一听,马上补发 5 号,不用傻等超时,大大缩短了反应时间。

拥塞控制管的是“路况”而非“肚子”。它让 TCP 在网络拥堵时自动减速、在网络空闲时主动提速,既不贪心也不怯场。正是这套探路逻辑,支撑了从手机弱网到数据中心高速互联的一切场景。

cwndnew={2×cwnd,慢启动阶段cwnd+1,拥塞避免阶段\displaystyle cwnd_{\text{new}} =\begin{cases}2 \times cwnd, & \text{慢启动阶段} \\cwnd + 1, & \text{拥塞避免阶段}\end{cases}
慢启动阶段窗口每轮翻倍增长,用于快速探明路况;到达阈值后改用线性增长,避免一头撞上拥堵悬崖。
  1. 慢启动翻倍在 cwnd 尚未到达阈值 ssthresh 时,每个往返周期窗口直接乘以 2,用指数速度抢占带宽。
    cwndnew=2×cwnd\displaystyle cwnd_{\text{new}} = 2 \times cwnd
  2. 阈值判定当 cwnd 达到或超过 ssthresh,发送方认为带宽已接近饱和,继续翻倍会引发丢包,于是切换策略。
    cwnd≥ssthresh\displaystyle cwnd \geq ssthresh
  3. 拥塞避免线性增长此后每轮只加 1 个 MSS,让窗口缓慢逼近网络极限,宁可保守也不冒进。
    cwndnew=cwnd+1\displaystyle cwnd_{\text{new}} = cwnd + 1

5. 性能调优(管道里能装多少杯)

假设奶茶店到顾客家的外卖路线,单程需要 10 分钟,路上每秒钟能跑 2 杯奶茶。那么当第一批奶茶还在路上时,这条“管道”里最多能同时运输多少杯?答案就是 10 分钟 × 每秒 2 杯 = 1200 杯。这个乘积叫“带宽时延积”(BDP),它代表链路上天然能容纳的在途数据量。你的窗口再大,超过 BDP 的部分也只会白白占用内存,因为管道已经塞满了;窗口太小,管道又喂不饱,速度上不去。理想窗口,就是刚好等于 BDP。

老式 TCP 的窗口字段只有 16 位,最大只能表示 65535 字节。在高速长距离链路上,这点窗口连 BDP 的零头都不到,于是 TCP 引入了“窗口缩放”选项——通过一个缩放因子把窗口值左移若干位,相当于把取餐号从 5 位数扩成了 10 位数。同时,接收缓冲区也不能无限调大:缓冲区越大,吞吐上限越高,但内存占用和丢包后的恢复代价也随之升高。生产环境里,你需要在两者之间反复权衡。

调优滑动窗口的过程,就像调整奶茶店和顾客之间的外卖运力:管道细了就加宽托盘,托盘宽了还得算好路上能装多少杯。理解了 BDP,你就知道“窗口越大越好”这句话错在哪里——关键是让窗口和管道匹配。

BDP=BW×RTT\displaystyle BDP = BW \times RTT
带宽时延积把“每秒能发多少”和“一个来回要多久”乘在一起,得到链路上最多能同时在途的字节数,是设置窗口大小的黄金基准。
  1. 带宽 BW链路每秒能承载的比特数,相当于外卖小路的“最大送餐速度”。
    BW\displaystyle BW
  2. 往返时延 RTT数据从发送方到接收方再返回的总耗时,相当于外卖一个来回的时长。
    RTT\displaystyle RTT
  3. 相乘得在途总量速度乘以时间正好是“距离”,放在网络里就是“在途字节数”,窗口若小于它则链路闲置,大于它则多余缓冲。
    BDP=BW×RTT\displaystyle BDP = BW \times RTT

6. 故障排查(外卖丢了多久该重做)

最后一杯奶茶送到半路,洒了。你作为店主,怎么判断它“可能丢了”、需要重做一杯?如果等太久才重做,顾客饿得慌;如果等太短就重做,奶茶可能其实还在路上,结果顾客收到两杯一模一样的,造成混乱。TCP 需要估算一个合理的“超时重传时间”(RTO)。它的聪明之处在于不做固定值,而是动态跟踪每一次往返的时间——就像你根据今天路况、天气、骑手习惯,不断修正你对“送达要多久”的判断。

这个判断基于两个统计量:平滑后的平均往返时间 SRTT,和往返时间的波动幅度 RTTVAR。RTO 取它们的加权和,既跟随平均值,又给波动留出余量。如果网络突然抖动,RTT 变大,RTO 也会自动放宽;如果网络平稳,RTO 会收敛到紧凑值,快速发现真正的丢包。另外,接收方连续发三个重复 ACK 也是一种“预警信号”——比超时更早地提醒发送方某个包丢了,发送方可以立即重传。

排查传输故障时,先看 RTO 是否设置合理,再看有没有大量重复 ACK 报到的预警信号,就能快速定位是链路震荡、还是接收方处理不过来。就像老店主盯紧骑手的送餐时长和顾客的催单记录,一切异常都能在数据里现出原形。

RTO=SRTT+4×RTTVAR\displaystyle RTO = SRTT + 4 \times RTTVAR
超时重传时间不是拍脑袋的固定值,而是由历史往返时间的均值 SRTT 和抖动 RTTVAR 联合算出,确保“既不过早重传,也不过晚发现”。
  1. 平滑平均值 SRTT对历史 RTT 做指数加权平均,近期的采样权重更大,代表“正常情况下一个来回要多久”。
    SRTT\displaystyle SRTT
  2. 抖动度量 RTTVAR统计 RTT 偏离平均值的幅度,波动越大该值越大,代表“这段路有多不靠谱”。
    RTTVAR\displaystyle RTTVAR
  3. 加权合成 RTO均值加上 4 倍抖动,相当于给正常时间留出充足的缓冲区间,覆盖绝大多数随机波动。
    RTO=SRTT+4×RTTVAR\displaystyle RTO = SRTT + 4 \times RTTVAR

AI 应用场景

1. 实时音视频传输的拥塞控制优化

阶段:模型推理与端到端服务部署

解决的问题:AI 语音助手、视频通话的音频流需要低延迟且抗抖动地传输。传统固定码率发送在弱网下手忙脚乱,丢包、卡顿频发。借助滑动窗口的拥塞控制思想,根据 RTT 和丢包率动态调整发送码率,让数据量始终贴着网络承载力走。

具体案例:某 AI 实时翻译 App 在 4G/5G 切换的瞬间,网络 RTT 从 30ms 飙到 300ms。工程团队在传输层引入类似 cwnd 的“动态码率窗口”:窗口收缩时自动降低音频码率,恢复时再逐步提升,最终把通话卡顿率从 5.2% 压到了 0.8%。

2. 大模型训练数据管道的 BDP 调优

阶段:数据预处理与分布式训练

解决的问题:分布式 AI 训练中,GPU 节点之间要高速同步梯度数据,几百 GB 的中间结果在高速网络上流动。若 TCP 窗口远小于 BDP,万兆网卡只能跑出百兆的效果。基于 BDP 调优窗口和接收缓冲区,能直接榨干硬件带宽。

具体案例:某智算中心在训练千亿参数大模型时,发现跨机通信吞吐只有 1.2GB/s。工程师按 BDP = 10Gbps × 120μs ≈ 150KB 重新配置窗口缩放因子和缓冲区,把吞吐拉满到 9.8GB/s,训练迭代耗时缩短了近 40%。

3. 金融交易系统的批量同步可靠性保障

阶段:在线推理服务的日志与状态同步

解决的问题:AI 风控系统需要把各节点的交易流水可靠同步到中心服务器,数据量大且不容丢失。累积确认和超时重传机制保证了批量传输的可靠性——一次确认一大批,既快又稳;零窗口机制则保护中心节点在高峰期不被海量数据冲垮。

具体案例:某银行 AI 反欺诈网关峰值每秒产生 20 万条事件,采用滑动窗口批量上报后,确认报文数量下降了 90%,且高峰期中心节点内存压力显著缓解,没有出现过一次因缓冲溢出导致的数据丢失。

Python 演示

python52 行
import time class SlidingWindow:    """简化版滑动窗口模拟器: 累积确认 + 窗口滑动"""     def __init__(self, window=4, total=12):        self.window = window        self.total = total        self.ack = 0     # 已确认边界, 指该序号之前的包都收到        self.sent = 0    # 已发送边界, 下一个要发送的序号     def fill(self):        """尽量发送, 填满窗口, 返回本轮新发送的序号列表"""        sent_now = []        while self.sent - self.ack < self.window and self.sent < self.total:            sent_now.append(self.sent)            self.sent += 1        return sent_now     def on_ack(self, ack_no):        """接收方返回累积确认, 窗口左边界前移"""        if ack_no > self.ack:            self.ack = ack_no        return self.ack     def free(self):        """窗口内还能容纳的新包数量"""        return min(self.window - (self.sent - self.ack), self.total - self.sent)  def simulate():    sw = SlidingWindow(window=4, total=12)    rounds = 0    packets_sent = 0    while sw.ack < sw.total:        rounds += 1        sent = sw.fill()        packets_sent += len(sent)        print("第", rounds, "轮: 发送", sent,              "| 窗口左边界 ack =", sw.ack,              "| 剩余可用 =", sw.free())        if sent:            ack_no = min(max(sent) + 1, sw.total)            sw.on_ack(ack_no)            print("    -> 收到累积确认: ", ack_no)        time.sleep(0.2)    print("滑动窗口完成共用", rounds, "轮;")    print("若用逐包停等协议则需要", packets_sent, "轮, 差距一目了然")  if __name__ == "__main__":    simulate()
目的:用 12 个数据包的传输过程,直观展示滑动窗口如何通过批量发送和累积确认,把 12 轮停等变成 3 轮流水线。
  1. while self.sent - self.ack < self.window and self.sent < self.total:只要窗口没满且数据还有剩余,就继续发,这是“填满托盘”的核心条件。
  2. self.ack = ack_no接收方一声“累积确认”,窗口左边界直接跳到新位置,右边自然腾出空位,窗口完成滑动。
  3. ack_no = min(max(sent) + 1, sw.total)模拟接收方确认“这批最后一个序号 + 1”,即该批之前全部安全到达。

常见误区

误区我以为发送窗口就像水管粗细,窗口越大,数据流得越快,所以直接把接收缓冲区和窗口调到最大准没错。

正解窗口超过带宽时延积(BDP)后,链路管道早已塞满,多出来的窗口只是躺在内存里睡大觉,还增加了内存压力和丢包后的恢复成本。就像奶茶店的托盘已经比外卖通道还宽,多摆的杯子一步也走不出去。正确做法是让窗口约等于 BDP,再留少量余量。

为何常见:人们直觉上把“窗口”理解成“水龙头开度”,忽略了链路本身存在物理容量上限,于是陷入越大越好的思维定式。

误区我以为接收方每次通告的窗口就是发送窗口,只要跟着它发就不会出错,不太清楚还有个拥塞窗口在暗中掌舵。

正解接收窗口(rwnd)反映的是接收方的“肚子容量”,拥塞窗口(cwnd)反映的是网络“路况承载”,二者角色完全不同。发送窗口取两者的最小值,任何一个收紧都会限制发送。比如接收方说能收 1MB,但路上只容得下 100KB,实际也只能发 100KB。

为何常见:初学者通常在抓包时只看到 rwnd 字段,忽视 cwnd 是发送方内部的动态变量,于是误以为窗口通告就是全部。

误区我以为“累积确认”只是把多个确认合并打包发送,本质还是逐包确认,只不过减少了一点报文数量。

正解累积确认的语义完全不同——它表示“该序号之前的所有字节全部收到”,而不是“序号对应的那一个包收到”。比如 ACK=100 意味着 0 到 99 号全部安全到达,哪怕中间有些 ACK 丢了也不影响。这就像顾客说“1 到 4 号都喝完了”,你不需要逐杯核销,一句话就搞定了四杯的账。

为何常见:受“TCP 面向字节流”抽象的影响,很多人天然用“包”的视角去理解确认,而忽略了确认号的区间语义。

误区我觉得超时重传时间越小越好,这样丢包能立刻发现并重传,网络延迟自然就低了。

正解RTO 太短会引发“假重传”风暴——数据其实还在路上,发送方却重复发了好几份,接收方收到一堆重复数据,网络更加拥堵,反而雪上加霜。RTO 应该基于 RTT 均值和抖动动态计算,留足安全余量,既不过早也不过晚。

为何常见:人们只盯着“丢包后的恢复速度”,却忽略了过早重传会制造虚假的额外流量,这是一种只看局部不看整体的常见思维陷阱。

误区看到接收方通告 rwnd=0,我第一反应是连接出了问题,应该直接断开重连,免得一直干等。

正解零窗口是流量控制的正常状态,表示接收方缓冲区暂时满了,连接本身完好无损。发送方会停止发数据,但会启动持续定时器周期性地发送 1 字节探测包,等待缓冲区腾空后恢复传输。贸然断开连接反而会造成不必要的重连开销和数据丢失。

为何常见:把“接收方忙不过来”误判为“链路故障”,源于对流量控制机制不熟悉,也反映了对 TCP 状态机理解不够全面。

面试题

Q1. 滑动窗口相比逐包停等确认,最大的效率提升来自哪里?

单选题 · easy

  • A.压缩了数据包的体积
  • B.允许连续发送多个数据包而不必逐个等待确认← 正确答案
  • C.增大了物理链路带宽
  • D.减少了接收方的内存

答案:B。停等协议发一个等一个,链路利用率极低;滑动窗口就像一个能放多杯奶茶的托盘,发完一批继续补一批,把等待时间重叠了起来。压缩数据、增大带宽都是误选项,它们与窗口机制无关,B 才是核心。

Q2. 最终实际发送窗口大小取的是哪两者中的较小值?

单选题 · easy

  • A.接收窗口与拥塞窗口← 正确答案
  • B.接收窗口与发送缓冲区
  • C.拥塞窗口与序列号
  • D.发送缓冲区与接收缓冲区

答案:A。接收窗口 Rwnd 代表接收方能吃下的量,拥塞窗口 Cwnd 代表网络能承载的量,发送窗口 Swnd = min(Rwnd, Cwnd)。就像奶茶店出餐既要看顾客肚子,也要看外卖路况,任何一方收紧都得跟着收紧。

Q3. 累积确认中,ACK 号为 N 表示什么含义?

单选题 · mid

  • A.序号为 N 的包已收到
  • B.序号 N 之前的所有数据都已收到← 正确答案
  • C.接下来要发序号为 N+1 的数据
  • D.只需要重传序号为 N 的包

答案:B。累积确认的 ACK 号表示“该序号之前的所有字节都已安全到达”,期望下一个收到 N 号。它就像顾客说“前 4 杯都喝完了”,而不是“第 4 杯喝完了”。A 和 C 都误解了确认号的区间语义,D 更是典型的逐包思维。

Q4. 当接收方通告 rwnd=0 时,发送方应当怎么做?

单选题 · mid

  • A.立即断开连接
  • B.忽略通告继续发送
  • C.停止发送数据,并启动持续定时器发送零窗口探测包← 正确答案
  • D.重传全部已发数据

答案:C。零窗口表示接收方缓冲区满了,连接正常,发送方要停手但保持心跳。持续定时器周期性发送 1 字节探测包,确认缓冲区腾空后恢复,防止 ACK 丢失造成死锁。A 是误判,B 会撑爆对端,D 毫无必要。

Q5. 带宽时延积(BDP)描述的是什么?

单选题 · mid

  • A.单个数据包的最大长度
  • B.链路上能同时在途的数据总量← 正确答案
  • C.链路的故障率
  • D.接收缓冲区的最小值

答案:B。BDP = 带宽 × RTT,它计算的是“管道里最多能塞多少数据”。就像外卖路线单程 10 分钟、每秒能跑 2 杯,管道里同时在途的奶茶就是 1200 杯。窗口小于 BDP 时链路喂不饱,大于 BDP 时多余缓冲毫无意义。

Q6. 以下哪些情况会触发 TCP 重传?

多选题 · mid

  • A.RTO 超时← 正确答案
  • B.连续收到 3 个重复 ACK← 正确答案
  • C.窗口向右滑动一格
  • D.收到零窗口通告

答案:AB。RTO 超时是“兜底重传”,等不到确认就重发;连续 3 个重复 ACK 是“快速重传”预警,接收方在喊“少了哪个包”。窗口滑动是正常传输推进,零窗口只是暂停发送,两者都不会触发重传。

Q7. 关于 TCP 慢启动算法,下列说法正确的有哪些?

多选题 · hard

  • A.cwnd 每个 RTT 翻倍增长← 正确答案
  • B.发生拥塞事件后,ssthresh 通常设为当前 cwnd 的一半← 正确答案
  • C.慢启动阶段 cwnd 线性增长
  • D.慢启动从一个较小的 cwnd 值开始← 正确答案

答案:ABD。慢启动从 1 个 MSS 开始,每个 RTT 翻倍,指数探路;到阈值后转入拥塞避免,才变成线性加 1。发生拥塞时阈值砍半是标准做法,C 把两个阶段的特征搞混了,所以不选。

Q8. “窗口滑动”这个说法直观上指的是什么?

单选题 · easy

  • A.发送方和接收方的确认边界序号持续向前推进← 正确答案
  • B.物理网卡在机箱内移动
  • C.数据包在链路上的位置移动
  • D.接收缓冲区的内存扩容

答案:A。窗口滑动是逻辑概念:已确认的序号从窗口左侧滑出,新的可发序号从右侧滑入,本质是确认边界的推进。网卡移动、内存扩容都是字面误解,数据包移动只是传输现象,与窗口状态无关。

Q9. 窗口缩放(Window Scaling)选项被引入的原因和效果是什么?

多选题 · mid

  • A.突破 16 位窗口字段 65535 字节的上限← 正确答案
  • B.适配高带宽高时延网络的巨大 BDP← 正确答案
  • C.缩短序列号的位数
  • D.取代累积确认机制

答案:AB。老式窗口字段只有 16 位,在大 BDP 链路上远远不够用,窗口缩放通过左移扩围,让窗口可以达到 GB 级。它不缩短序列号位,也不替代累积确认,C、D 都是干扰项。

Q10. 估算 RTO 超时重传时间时,主要综合了哪些统计量?

单选题 · hard

  • A.平滑平均往返时间 SRTT 与往返抖动 RTTVAR← 正确答案
  • B.当前发送缓冲区剩余量
  • C.最近一次窗口通告值
  • D.已确认的数据包总数

答案:A。RTO = SRTT + 4 × RTTVAR,均值反映正常耗时,抖动反映网络波动幅度,两者结合才能在“过早重传”和“过晚发现”之间取得平衡。缓冲区、窗口通告、确认总数都与路径时延的统计特性无关。