SACK

在传统的TCP确认机制中,接收方只能发送一个累积确认,告诉发送方它已经收到了某个序列号之前的所有数据。例如,发送方发送了数据包1、2、3、4、5,如果数据包3丢失了,接收方只能发送ACK 3,表示它收到了2,但期待3。发送方收到ACK 3后,并不知道3后面的4和5是否已经收到,它会重新发送3以及3后面的所有数据包。这样原先已经正确传输的TCP报文段也可能重复发送,降低了TCP性能。

SACK技术正是为改善这种情况而产生的,它使TCP只重新发送丢失的TCP报文段,而不用发送所有未被确认的TCP报文段

有了SACK,接收方可以有选择地告诉发送方,它已经收到了哪些非连续的数据块。这样,发送方就只需要重传丢失的数据包,而不需要重传已经收到的数据。

SACK Permitted

是否启用SACK需要收发双发在建立连接时进行协商,通信双发在SYN段或SYN+ACK段中添加SACK允许选项通知对端本端是否支持SACK,如果双发都支持,那么后续连接态通信过程中就可以使用SACK选项了。所以SACK允许选项只能出现在SYN段中。

+---------+---------+
| Kind=4  | Length=2|
+---------+---------+

SACK Option

连接建立后,接收方就可以通过SACK选项告诉发送方字节的实际接收情况。SACK选项格式如下:

0        1        2        3        4
                  +--------+--------+
       NOP        | Kind=5 | Length |
+--------+--------+--------+--------+
|      Left Edge of 1st Block       |
+--------+--------+--------+--------+
|      Right Edge of 1st Block      |
+--------+--------+--------+--------+
|                                   |
/            . . .                  /
|                                   |
+--------+--------+--------+--------+
|  Left Edge of nth(max=4) Block    |
+--------+--------+--------+--------+
|  Right Edge of nth(max=4) Block   |

由于整个TCP首部的选项部分不能超过40字节,所以一个ACK段中最多可以容纳4组SACK信息。

  • Left Edge表示已收到的不连续块的第一个序号
  • Right Edge表示已收到的不连续块的最后一个序号+1,即左闭右开区间。

通过ACK和SACK信息,发送方就可以确定接收方具体没有收到的数据就是从ACK到最大SACK信息之间的那些空洞的序号。

D-SACK

D-SACK(Duplicate-SACK),即重复选择性确认,是为了解决TCP协议中由不必要的重传和对乱序数据包的错误判断所引起的问题。

  1. 假丢包问题: 在快速重传机制中,发送方连续收到三个重复ACK时,会推断某个数据包丢失了,并立即重传。但有时候,数据包并没有丢失,只是因为网络拥塞或路由变化导致乱序到达。在这种情况下,发送方不必要的重传会浪费带宽,并可能加剧网络拥塞。
  2. 不确定性问题: 发送方在重传数据包后,不确定接收方是收到了原始数据包还是重传的数据包。这种不确定性会影响发送方对网络拥塞程度的判断,从而可能导致拥塞控制算法做出错误的决策。

D-SACK让接收方可以明确告诉发送方它收到了哪些重复的数据包,从而帮助发送方区分真丢包和假丢包,并做出更精确的拥塞控制决策。

D-SACK利用SACK选项的格式,将重复接收到的数据块信息反馈给发送方。

SACK和D-SACK使用相同的SACK选项格式,但发送方会根据SACK Block的序列号范围来进行判断。

  • SACK Block报告的是:发送方未收到确认的数据。
  • D-SACK Block报告的是:发送方已经收到确认的数据,或者已经重传过的数据。

D-SACK的工作原理:

  1. 假设发送方发送数据包 1, 2, 3, 4
  2. 数据包 2 在网络中发生乱序,但没有丢失。 它比 3 和 4 晚到达。
  3. 接收方首先收到了 1, 3, 4。
  4. 接收方收到 1 后,发送 ACK 2(表示期望收到 2)。
  5. 接收方收到 3 后,发现 2 还没到,它会发送一个包含SACK选项的TCP报文,其中ACK = 2, SACK Block = [3, 5)。表示从3到5(不包含5)的数据块已经收到。
  6. 发送方收到多个 ACK 2,认为数据包 2 丢失了,于是触发快速重传,重新发送数据包 2。

上面是SACK的流程,当接收方收到了之前乱序的原始数据包2。此时,接收方收到了两个数据包2。此时D-SACK介入

  1. 接收方会再次发送一个TCP报文,其中包含一个特殊的SACK Block,这就是D-SACK。
  2. 其中ACK = 5, SACK Block = [2, 3)。用来标记重复接收到的原始数据包 2。
  3. 发送方收到这个带有D-SACK信息的报文后会发现SACK Block [2, 3) 标记的数据段,是它之前已经发送过且认为丢失了的。
  4. 发送方明白了:数据包 2 其实并没有丢,只是乱序了,它之前进行的重传是多余的。

通过这个过程,D-SACK就为发送方提供了精确的信息,使其能够:

  • 不计入重复重传的数据包到其拥塞窗口中。
  • 更准确地判断网络是拥塞(真丢包)还是只是数据乱序(假丢包)。

当然,没有具体规定发送端对D-SACK的处理方式,不同的TCP实现可能会有不同的行为。但总体来说,D-SACK为TCP协议提供了一种机制来更好地处理网络中的不确定性,提高了传输效率和网络性能。