Skip to content

TCP机制

约 2307 字大约 8 分钟

2025-12-21

image

重传机制

超时重传

发送一个数据时设定一个定时器,若超过指定时间后没有收到这条消息的 ACK,将会重新发送该数

发生的情况

从机制中可知,一旦没收到 ACK,就会触发,两种情况下会导致该情况

  • 数据包丢失,没能抵达目标机
  • 数据包抵达目标机,但其传回的 ACK 丢失

超时重传时间的设置

超时重传时间(RTO) 过小会导致不必要的重传,过大会导致传输效率变低 实际设置中,RTO 应略大于 RTT

快速重传

image 当连续多次收到相同的 ACK,说明当前网络环境并无问题,丢失只是因为网络波动,因此可以不考虑 RTO 直接进行重发 但假如 Seq2 和 Seq3 都丢失了,当 Seq6 的第三个 ACK2 到达时,重发 Seq2,但 Seq3 依旧需要等到三次 ACK3 才能会重发,因此这时就需要面临重传一个,还是重传所有的问题

SACK

Selective Acknowledgment 选择性确认 在 TCP 的头部「选项」中增加SACK字段用于接受者告诉发送者我收到了哪些数据 image

SACK 通过定义一个个数据块(SACK Block) 来实现,每个数据块由两个指针边界组成,要表示范围,最直接的方式就是声明上下界,TCP 头部中的序列号为 32 位,即 4 字节,那么一组上下界需要占用 8 字节 此外为了声明这是 SACK 的选项以及总长度,选项头部需要占用 2 字节 这意味着一个 TCP 报文的 40字节选项最多可以携带 (402)/8=4.75(40 - 2) / 8 = 4.754 个 SACK Block

Duplicate SACK

用于向发送者报告收到了重复的数据端 例如发送者的超时重发因为丢包问题没能收到接受者的 ACK,发送者会持续进行重传导致接收者收到了重复的数据,接收方据此向发送方报告

作用
  • 检测不必要的重传
  • 帮助判断网络的状态
  • 某些 TCP 拥塞算法会利用 D-SACK 改善因误判导致的发送速率降低

滑动窗口

若 TCP 每次发送都要等到上一个 ACK 的到达,传输效率无疑会与 RTT 成反比,因此引入窗口的概念 窗口范围内的数据无需等待 ACK 而可以继续发送数据,相当于同时并发一大批请求 因为数据的处理速率与两端都有关系,且最高的传输速率由处理速率最低一端决定 image 当最低序号数据确认收到后窗口才会后移 image 使用两个绝对指针SND.UNA(Unacknoleged)和SND.NXT控制发送窗口的起始位置与可用窗口的起始位置 使用SND.WND控制发送窗口的大小 可用窗口大小 = SND.WND - (SND.NEX - SND.UNA)

流量控制

在 TCP 数据发送的过程中,接收方会根据缓冲区大小以及系统实时的处理能力动态调整窗口大小

在以下例子中接收方根据需求动态调整窗口大小,而发送方也根据窗口大小进行调整 image

而因为发送方与接收方存在 RTT,调整的信息并不能马上进行同步 在以下的例子中,接收方调整窗口大小后接收到了调整前发送的数据,若这一数据大大小大于当前的窗口,则会直接将其丢弃 image TCP 会为连接的 SND.UNA 设定 RTO,当 RTO 被触发时根据当前的窗口大小重发数据,当 SND.UNA 滑动时,若 [SND.UNA, SND.NXT] 内存在数据,则为 SND.UNA 重置 RTO,否则关闭 RTO

窗口关闭

当窗口为 0 时,发送端会停止向接收端发送数据,若在关闭后接收端需要恢复窗口,而这一 ACK 丢失了,则可能会造成「死锁」 image 为了解决这个问题,发送方在收到零窗口的消息后会启动一个持续计时器,当计时器超时且接收端仍未发送调整请求时,发送端会发送窗口探测报文,告诉对方我当前的窗口仍为零窗口

糊涂窗口综合症

指的是接收方无法以匹配的速度处理缓冲区中的数据造成传输的效率较低。例如发送方一次能够发送 200 字节的数据,而接收方在 RTT 内只能只能处理100 的字节数据,那么在有限的缓冲空间内,接收窗口将会不断减少,最终会造成每次发送的数据很小,而 TPC+IP 就有 40 个字节,也就会出现传输的性价比过低

问题解决

  • 接收方避免通告允许小窗口 当接收窗口的大小小于 min(MSS,缓存空间/2)min(MSS, 缓存空间/2) (两者中的最小值)时,就会向发送方通告窗口为 0,即让发送方暂停发送数据,等到接收窗口足够大时再向发送方通告窗口打开

  • 发送方避免发送小数据 使用 Nagle 算法只有在满足一下两条件中的一条才能够发送数据

  1. 窗口大小 >= MSS 且 数据大小 >= MSS
  2. 收到之前发送数据的ACK回包,也就是接收端要求继续发送

什么是 MSS

MSS(Maximum Segment Size, 最大报文段长度)是 TCP 三次握手连接时通过 SYN 报文中的选项字段协商决定的,通常设置为 MSS=MTUIP头部TCP头部MSS = MTU - IP头部 - TCP头部 TCP 并不希望由 IP 层进行低效的分片,因此在三次握手时双方相互通报本机的 MTU,两者取最小的值计算后得到 MSS 若瓶颈存在于传输过程中的路由上,系统引入了路径 MTU 发现(Path MTU Discovery, PMTUD),要求 IP 层封包时将 DF 设置为 1,当有路由的 MTU 小于当前 IP 包的大小时,会通过 ICMP 协议发送一个错误报告并通告当前路由的 MTU,以此方式探测出链路上最窄的瓶颈

拥塞控制

当网络出现拥堵时会造成数据包的时延和丢失,如果继续发送大量的数据包只会照成更严重的丢包,进而引发重传形成恶性循环 而 TCP 为了防止这种状况的出现,会自行降低数据量 因此拥塞控制的目的是避免「发送方」的数据填满整个网络 拥塞窗口是由发送方维护的一个状态变量 swnd = min(cwnd, rwnd)

  • swnd:发送窗口
  • cwnd:拥塞窗口
  • rwnd:接收窗口(即流量控制中的SND.WND,对应物理可容纳的最大值) 若网络中出现了拥塞,则cwnd减小;若网络中没有出现拥塞,则cwnd增大

慢启动

发送方每接收到一个 ACK,拥塞窗口cwnd的大小就会加一 image 拥塞窗口以指数性的增长 但显然不会让拥塞窗口无限增长,因此存在一个叫慢启动门限ssthresh(slow start threshold)的状态变量

  • cwnd < ssthresh时,使用慢启动算法
  • cwnd >= ssthresh时,会使用「拥塞避免算法」

拥塞避免算法

每收到一个 ACK 时,cwnd增加 1/cwnd1/cwnd 相较于慢启动,cwnd的增长变为线性增长,直到网络进入拥塞状况,当出现了丢包重传后将会使用「拥塞发生算法」

拥塞发生

重传机制

  • 超时重传
  • 快速重传 对于两种重传机制的发生需要使用拥塞发生算法或快速恢复算法

拥塞发生算法

当发生「超时重传」时,会改变ssthreshcwnd的值

  • ssthresh设为cwnd/2
  • cwnd重置为初始值 image 这种方式非常激进,数据流会立刻降低

快速恢复

快速回复算法认为若可以连续收到 3 个重复的 ACK,即出发了快速重传机制,说明网络状态并没有那么糟糕,因此并不需要那么激进的调整

  • ssthresh = cwnd/2
  • cwnd = ssthresh + 3(因为触发快速恢复算法前会触发快速重传机制,而每个 ACK 会使cwnd加一,因此加三) cwnd = ssthresh + 3的目的是为了快速的进行恢复,而在接收到新数据的 ACK 后,需要将cwnd恢复至慢启动启动门限,推出快速恢复状态 image
为什么要退出快速恢复状态而重置 cwnd?

完成快速恢复后将发送速率恢复到一个安全的水平,避免窗口过大再次引发拥塞

参考

TCP特性

#review