在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。但在错综复杂的网络,并不一定能那么顺利能正常的数据传输,万一数据在传输过程中丢失了呢?TCP 针对数据包丢失的情况,会用重传机制解决。
常见的重传机制:
超时重传
在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK
确认应答报文,就会重发该数据,也就是我们常说的超时重传。
触发机制
TCP 会在以下两种情况发生超时重传:
- 数据包丢失
- 确认应答丢失
超时时间
RTT
(Round-Trip Time 往返时延)
RTO
(Retransmission Timeout 超时重传时间)
对于RTO
「较长或较短」有两种超时时间不同的情况:
- 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
- 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。
根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。
RTT的计算
Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。
ps:如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍,也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
快速重传
快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。
触发机制
快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
引发的问题
重传的时候,是重传一个,还是重传所有的问题。(例如上图是只重传Seq2 ,还是Seq2 ,Seq3,Seq4,Seq5都重传)
-
如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。
-
如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5的报文是已经被接收过了,对于重传 Seq4 ~Seq5 折部分数据相当于做了一次无用功,浪费资源。
SACK 方法
SACK
( Selective Acknowledgment), 选择性确认。
在 TCP 头部「选项」字段里加一个 SACK
,它可以将已接受那部分的信息发送给[发送方],这样发送方就可以只重传丢失的数据。
触发机制
发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制。并通过 SACK
信息发现只有 200~299
这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复。
ps:如果要支持 SACK
,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack
参数打开这个功能(Linux 2.4 后默认打开)。
D-SACK
D-SACK 全称Duplicate SACK,其使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
ACK 丢包触发
- 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据包(3000 ~ 3499)
- 于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」 3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着
D-SACK
。 - 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。
网络延时
- 数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
- 而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」;
- 所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包。
- 这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了。
总结
D-SACK
有这么几个作用:
- 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
- 可以知道是不是「发送方」的数据包被网络延迟了;
- 可以知道网络中是不是把「发送方」的数据包给复制了;
ps:在 Linux 下可以通过 net.ipv4.tcp_dsack
参数开启/关闭这个功能(Linux 2.4 后默认打开)。