阅读视图

发现新文章,点击刷新页面。

你应该了解的TCP滑窗

一、TCP 滑窗机制:从可靠传输到高效流控的演进

在基本的 TCP 传输概念中,滑动窗口机制通过序列号确认实现了可靠数据传输。然而,这仅仅是故事的开始。真正的 TCP 效率来自于对窗口机制的深度优化和策略控制,其中蕴含着精细的设计权衡。

二、累计确认:效率与可靠性的完美平衡

TCP 并没有采用简单的"一对一"确认机制,而是设计了精巧的累计 ACK 策略。这种机制的核心思想是:确认号表示该序号之前的所有字节都已被正确接收

已接收:1-1000, 2001-3000  // 序列号1001-2000形成空洞
期待接收:1001

在这种情况下,即使收到了 2001-3000 的数据,接收端仍然只能确认 1001。只有当 1001-2000 的数据到达后,才能一次性确认到 3001。

三、延迟确认的优化策略

TCP 实现中通常包含延迟确认计时器(通常 200ms,也许更短):

  • 当按序数据到达时,不立即回复 ACK,而是等待短暂时间
  • 如果在此期间有后续数据到达,可以合并确认
  • 如果期间有发送数据,可以捎带确认
  • 超时后仍会发送纯 ACK

这种策略在实际网络中可以将 ACK 数量减少 50% 以上,显著降低网络开销。

四、滑窗内部结构:字节级的精确控制

与概念性理解不同,实际的 TCP 滑窗是以字节为单位进行管理的,这种精细度带来了更精确的流量控制。

发送方窗口结构:

WeChatWorkScreenshot_4a6f5045-0724-4e93-8a2d-79f9a39d6e61.png

接收方窗口结构:

image.png

接收方多出的"已接收未提交"部分反映了应用层处理延迟。当应用读取速度跟不上接收速度时,这部分会增长,导致通告窗口缩小,这一“空间”的减小会导致接收方的通告窗口急剧下降,这时需要一个反馈机制,告诉发送方:减速->暂停。

五、流量控制:动态的速率协调机制

在第四点我们说到了滑窗结构,发送窗口与接收窗口,在一个完整的发送流程中我们会有探测 -> 启动 -> 连接 -> 发送 -> 接收 -> 回复 -> 确认 等一系列过程。

当然我们本文的重点在于滑窗,是这个体系知识的一小部分,那滑窗是如何动态控制的? 简单来说就是要保证整个通信链路能高效传输,既不能太慢也不能太快,它要在一个阀值附近横跳保证网络以最大可通信速率运行。

这个由谁决定?是拥塞窗口(cwnd)来进行控制,主要利用了慢启动、拥塞避免、快速重传和快速恢复这四个方案来处理

1:慢启动

其实说白了就是一开始不要把传输速率跑满而是从0加速,快速的接近一个半窗口阀值,因为一开始跑满速率这很可能会导致整个链路在开始就无比拥挤,才刚开始就堵车了这不是一个好消息,我们更希望的是先发一小部分内容用较低的速率发出,并且尽快达到半窗口阀值,而后逐步加速的过程。

2: 拥塞避免

接在慢启动之后拥塞避免方案会马上接手传输,在此基础上均匀加速到最大阀值是一个线性的过程

WeChatWorkScreenshot_00e4b9c9-439d-4c82-a21f-b219b8a452bf.png

以上半窗口阀值就是图中的ssthresh,慢启动就是达到ssthresh这段,而拥塞避免是ssthresh到max这段 这里我们也可以看到长短连接区别,长连接效率会高很多,短连接相当于每次都要有一个加速的过程,非常耗时。

3: 快速重传

没有快速重传时,TCP 发送方像个死脑筋:只有一个重传定时器。如果数据包丢了,它必须等到定时器超时才会重传。这通常需要几百毫秒到几秒,效率极低。

快速重传的核心思路就是:  别等定时器了!如果接收方反复在问同一个问题,那肯定是有包丢了!


想象你是个发送方,你发了 1, 2, 3, 4, 5, 6 号包。

  • 接收方顺利收到了包1,它回信说:“包1收到了,我下一个想要包2!”(ACK 2)
  • 这时,包2在网络中丢了,没收到。
  • 接收方紧接着收到了包3。它一看,包2还没到呢,包3就来了?但它最关心的还是包2。于是它再次回信催更:“包1收到了,我下一个还是想要包2!”(ACK 2)
  • 接着,它又收到了包4。它继续回信:“我想要包2!”(ACK 2)
  • 然后又收到了包5,它依然回信:“我想要包2!”(ACK 2)

对于发送方来说,它在短时间内连续收到了 3个重复的 ACK 2(加上第一个正常的,一共是4个 ACK 2)。

第二步:发送方触发“快速重传”

发送方内部有个小本本,专门记着 重复ACK计数器

  • 收到第一个 ACK 2:正常,不管。
  • 收到第二个 ACK 2(第一个重复ACK):计数器+1。心想:“有点奇怪,但再等等。”
  • 收到第三个 ACK 2(第二个重复ACK):计数器+1。心想:“情况不妙,可能真丢了。”
  • 收到第四个 ACK 2(第三个重复ACK) :计数器变成3了!发送方立刻判断:“实锤了!包2肯定丢了!别等重传定时器了,现在就重传包2!”

这个神奇的阈值 3(即收到总计4个相同的ACK)是经过大量实践验证的,能有效避免因为网络短暂乱序而误判丢包。

4: 快速恢复

重传完丢失的包2之后,故事还没完。如果直接回到原来的状态,可能会让网络瞬间再次拥塞。所以 TCP 会紧接着启动 快速恢复 算法:

  1. “假装”事情没那么糟:既然能收到这么多重复ACK,说明网络还能通,只是丢了一个包。所以不像超时重传那样把窗口直接打到1(慢启动),而是只砍半
  2. 保持数据流:在重传期间,发送方还可以继续发送新的数据(比如包7,包8),因为接收方的缓存里还存着包3,4,5,6,窗口并没有被完全占满。

当发送方收到针对包2(以及之后数据)的新ACK(比如 ACK 7),表明丢失的包已经被成功弥补,它就退出快速恢复状态,恢复正常传输。

WeChatWorkScreenshot_ea45b90a-a236-4116-b43c-c53880d890c4.png

上图可以看到,这是一个非常精巧的设计,确认丢包马上降速至新阀值,启动拥塞避免逐步恢复,有人会问为啥丢包就要降速?其实不降速是不行的,这会导致网络拥堵灾难,丢包是在告诉你路堵,已经导致路上的某些汽车已经冲出了道路找不到了。这个是一个非常危险的信号,出于公平使用网络tcp会自动降速,保证网络稳定通畅。


总结:TCP 滑窗的演进

TCP 滑窗机制从最初的基本可靠传输,演进为一套复杂的流量控制和效率优化系统。这种演进体现了网络协议设计的核心智慧:

  • 渐进优化:通过累计确认、延迟确认等机制在保持兼容性的前提下不断提升效率
  • 自适应控制:根据网络状况和应用需求动态调整传输策略

这些设计使得 TCP 在三十年后的今天,依然是互联网不可替代的传输基石。我们需要理解,每一种你觉得没必要的做法背后都是一次小型灾难,如今这些策略已经非常成熟,稳定的服务我们的每一次网络通信中。

注明:文中图片来自互联网

❌