> 文章列表 > TCP 的 NACK 与 SACK

TCP 的 NACK 与 SACK

TCP 的 NACK 与 SACK

可靠传输,一旦丢包,必然 HoL blocking,NACK 和 SACK 必须二选一驱动重传:

NACK:receiver 主动通告没有收到的报文,sender 收到后重传。

SACK:receiver 主动通告收到的报文,sender 收到后 mark lost 重传。

到底哪个好?

TCP 为什么选择 ACK/SACK 而不是 NACK?

争论不休,回答当然是怎么解释都通。一个人们不愿接受的事实是,只要都可以解决问题的二选一,怎么解释都通,一方优势恰是另一方缺陷,创造了很多可以扯淡的话题,当然,这个现象在针对 TCP 的讨论中尤为明显。

可是谁告诉你们 TCP 基于 ACK/SACK 了?它明明是个 NACK/SACK 协议啊。

人们认为 TCP 是 ACK/SACK 主要因为 TCP 头里的确认号就叫 ACK,且后来引入的选择确认叫 SACK。但事实上 TCP 头里的 ACK 表示的是一个 NACK,receiver 尚未接收到的一个序号,就是一个 Hole。

即便如此,可为什么后来选择确认选择了 SACK 而非 NACK,比如:
TCP 的 NACK 与 SACK
因为 TCP Option 空间有限,无论 NACK 还是 SACK 都只能包含有限段,以 4 段为例,实际上就是大小为 4 的滑动窗口,而 NACK 和 SACK 的解释是截然相反的:

NACK:迄今为止的 Hole,向前积累,下一刻若有 Hole 被填充,需要撤销曾针对它的 NACK;

SACK:从今往后的确认,向后积累,只要当下被 SACK,后面一直生效(暂忽略 Reneging 这不合时宜的家伙);

Option 空间只能放 4 段(or any)的约束下,NACK 要容纳新 Hole,要删掉一个旧 Hole,而该旧 Hole 如果接下来被接收,需要撤销它的 NACK,而此撤销大概率在 NACK 后被 sender 收到,产生不必要重传。

SACK 完全没有撤销问题,容纳新 SACK block,只需删去一个旧的(建议采用 LRU),除非丢掉连续 4 个 SACK,每个 SACK block 都能以 4 的冗余度被 sender 接收。

关于可靠传输的 NACK or SACK,TCP 建议了两个原则

原则一:连续空间用 NACK,表示积累确认,NACK 下一个要收的;非连续空间用 SACK,滑动选择确认已经收到的。

原则二:统一由 sender 进行丢包判断和重传,重传者判断丢包。

下个问题,Option 只有 4 段,是缺陷吗?

并不是。

因为这 4 段 SACK block 是由 receiver 非延迟滑动选择确认的,关键词 “非延迟确认”,“滑动”,“选择确认”,只要收到一个不连续段,马上 SACK,接收和 SACK block 是一比一兑换,其实 1 段就足够,4 段只多提供了 3 个冗余。

不过遗憾的是,TCP 在 receiver 端有一个 Reneging 机制,违背了上述广义的原则二,算是画蛇添足。 若非 Reneging,SACK 就永久向后积累了,sender 便可放心删除 retrans-queue 里的 SACK block。

有人说 NACK 丢失必等超时,这并不算 NACK 的缺陷,SACK 同样也一样,TCP 不对 SACK 进行 ACK。只要有源源不断的流量被 receiver 接收,无论 NACK 还是 SACK 都会被触发。且超时并非坏事,它本身就闭环,最初的 TCP 并没有 fast retransmit,超时是唯一的重传触发机制。

没有 fast retransmit 机制的 TCP 相当于对 NACK 不处理。当发生 HoL blocking,receiver 收到乱序报文会发生 dupack 作为 NACK 提示 sender,这是一个多么明显的 NACK 信号,以至于 fast retransmit 这个伟大的重传机制被引入 TCP。

如果细看 RACK 后的 SACK,其实它也是一个 NACK 信号,提示 sender “在某时间 X 之前的报文均未接收到” 这明确的 NACK,虽然它在形式上依然表现为 SACK。应用 NACK or SACK 的原则一,“连续空间用 NACK”,由于时间序标识的 RACK 连续了,便可使用 “积累 NACK” 了。

对于可靠传输,NACK 才是自然的,SACK 只是不得不。

在设计可靠传输协议时,经常有人会面临 NACK or SACK 的选择,其实当提到这两个词时,基本就暗示了几个误区,TCP 没有采用 NACK 是因为 … SACK 比 NACK 好,由于 TCP ACK 时钟的存在,你会认为连续的积累确认是理所当然的积累 SACK,或者直接叫积累 ACK,但只要砍断 ACK 时钟,给 TCP 头的 ACK 字段换一个名字,其实 TCP 明明就是一个 NACK 协议,ACK 字段表示的是 una(unacknowledged…)

浙江温州皮鞋湿,下雨进水不会胖。