TCP/IP的确认号,序列号和超时重传的学习笔记
一:确认应答和序列号
在tcp中,发送端的数据到达主机时,接收端会返回一个已收到的通知。这个消息叫做确认应答(ACK)。
当发送端发送数据后,会等待对端的确认应答。如果有确认应答,说明数据已经成功到达。反之,则数据丢失的可能性很大。
发送端一定时间内没有等到确认应答,发送端就认为数据已经丢失了,就可以重发数据。因此,即使产生了丢包,也能保证数据到达对端。实现可靠传输。
想一下:为什么发送端一定时间内没有收到确认应答呢?有哪些原因呢?
这里有几个对象:
1、发送端,发送的数据
2、对端,回应的ACK数据
3、中间的网络
第一种 发送端:发送端的数据根本没有到达对端,发送的数据在途中就已经丢失了。
第二种 对端:可能数据对方已经收到了,只是回应ACK的数据时,途中丢失了。这种情况会导致发送端没有收到对端的确认应答。
第三种:网络:由于网络拥堵,确认应答ACK延迟到达了。或者发送端的数据延迟到达对端了。这种情况也可能导致重发数据包。
上面这些情况都有可能导致发送端没有收到对端的ACK应答。
上面说了, 如果需要发送端重发数据,那需要重发哪部分数据,发送端怎么知道重发哪部分数据?
这个就是序列号功能了。
什么是序列号:
序列号是按照顺序给发送数据的每一个字节都编号。
接收端接收数据TCP首部中的序列号和数据长度,将自己下一步接收的序号作为确认应答发送回去。
这样,通过序列号和确认应答号,就可以实现TCP的可靠传输。
上面谈到了重发数据情况,那什么时候重发数据呢? 这个时间怎么计算?
也就是说,这个重发的时间怎么确定?什么时候开始计时的?
二:重发和重发超时时间
重传定时器
要实现超时重发,就是在发送完一组数据时,就立即给这组数据设置一个超时定时器。如果定时器到期之前收到了对端的确认号,就撤销重传超时定时器。
超时时间怎么设置:
- 如果设置的长一点,重发数据就变慢了,效率就变差了;
- 如果设置的短一点,可能会导致数据还没丢就重发了。这样的话,重发数据就变快了,自然会增加网络负担,导致网络拥塞,一拥塞,就会导致更多的超时,超时又导致更多的重发。
所以这个时间的设置就变得很重要了。
先看看发送数据的几个步骤:
- 发送端发送数据,并启动重传定时器
- 等待对端的应答ACK号
- 重传定时器到期
这里有几个时间的概念:
- RTT(Round Trip Time):发送一个数据包到收到对端的ACK所花费的时间
- RTO(Retransmission TimeOut): 发送数据包,启动重传定时器,重传定时器到期所花费的时间,称为RTO
网络又是复杂的,由各种主机,路由器,网络等组成。所以这个重传时间RTO只能动态来计算了。
经过一些计算机专家的研究,它是根据RTT来动态计算的。
重传时间算法
第一种经典算法:RFC793
- 首先计算一个平滑的RTT称为SRTT, alpha是一个平滑因子,取值为0.8或者0.9
SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)
- 基于SRTT,计算出对应的RTO
RTO = min[UBOUND, max[LBOUND, (BETA*SRTT)]
其中UBOUND是最大值,一般情况下为120s,LBOUND是最小重传值,一般情况下为1s,Beta也是一个因子,建议取值为1.3~2.0.
这个算法来自 RFC793
但是这个算法在目标Linux中没有使用,原因是存在不足。有文章指出:从公式可以看到在经典的RTO计算方法中会有一个很大的问题,就是当RTT对RTO的影响太小了,也就是说经典的RTO计算方法在RTT变化比较大的网络中,会表现的非常不好的。
第二种算法:Jacobaon/Karels 算法
1988年,Van Jacobson和Karels在Congestion Avoidance and Control这篇论文中提出一种新的算法RFC6298,这里对于rtt的采样,多添加了一个因素,那就是均差(mean deviation),而这里并不是传统意义上的均差,使用的平均数其实就是我们在经典方法中的srtt。
第一次RTO计算方法, 假设RTT = R
1. SRTT = R
2. RTTVAR = R/2
3. RTO = SRTT + max(G, K*RTTVAR) , K = 4
后续的RTO计算,假设当前的RTT为R'
RTTVAR = (1 - beta)*RTTVAR + beta*|SRTT - R'|
//计算平滑RTT和真实RTT的差距,切记这个地方的SRTT是上一次的SRTT
SRTT = (1 - alpha)*SRTT + alpha*R'
//计算平滑RTT, 及是SRTT
RTO = SRTT + max(G, K*RTTVAR)
//计算 RTO
alpha = 1/8 beta = 1/4,值得指出的是这个算法在目前的Linux协议栈中应用
第二种算法到底有啥好处,可以去查看相关文章看看