TCP协议探究(二):超时与重试
点击👆蓝色“ 深入原理”,关注并“设为星标”
技术干货,第一时间推送
1 概述
TCP提供可靠的运输层。
可靠性保证之一:确认从另一端收到的数据。
但数据和确认都有可能会丢失。TCP通过在发送时设置一个定时器来解决这种问题。
如果当定时器溢出时还没有收到确认,它就重传该数据。
TCP对于每个连接TCP管理4个不同的定时器:
重传定时器:使用于当希望收到另一端的确认。
2MSL定时器:测量一个连接处于TIME_WAIT状态的时间。
坚持(persist)定时器:使窗口大小信息保持不断流动,即使另一端关闭了其接收窗口
保活(keepalive)定时器:用于检测一个空闲连接的另一端何时崩溃或重启。
2 TCP的状态机
超时情况:
建立连接时SYN超时:
client主动打开连接,发送SYN报文给server
server收到后被动打开,发送SYN+ACK报文给client,此时client直接下线
导致server无法收到client的ACK报文,即该TCP连接既没有建立也没有断开,所以server又会重传SYN+ACK报文
在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才会把断开这个连接。
SYN Flood:
减少Server的重试次数
增大SYN的连接数
处理不过来的连接直接丢弃
恶意伪造大量的TCP连接并断开Client,导致Server维持了大量的TCP连接,且需要63秒后才会断开,即63秒内的TCP连接数足够多时,Server将承受不住,导致正常的连接不能处理。
处理方案:
ISN的初始化:
ISN不是每次建立建立连接时都从1开始
问题:因为如果从1开始,client发送了30个segment给server,此时网络断开,过会client重连,又使用ISN=1来发送报文,但之前的报文已经送到了,即server认为client的ISN(初始化序列号)为30,但是实际上为1。
解决方法:ISN会和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。
MSL(Maximum Segment Lifetime):TCP段的最大存活时间,4ms*2^32=4.55小时,即TCP段存活时间不超过4.55小时就不会重用ISN。
MSL 和 TIME_WAIT:
在TCP的状态图中,从TIME_WAIT状态到CLOSED状态,有一个超时设置,这个超时设置是 2 * MSL。
TIME_WAIT(2 * MSL):确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到Ack,就会触发被动端重发FIN,一来一去正好不超过2 * MSL。
有足够的时间让这个连接不会跟后面的连接混在一起(有些路由器会缓存IP数据包,导致连接被重用)。
3 TCP重试机制(发送方)
(1)概述
TCP要保证所有的数据包都到达,必需有重传机制。
发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办?
(2)超时重传机制
不回ACK,一直等待SN=3的到来,而发送方一直收不到3的ACK,就会超时重传3.
这种方式会有严重的问题,那就是死等3,所以导致4和5已经被收到了,而发送方也完全不知道(没有收到ACK),所以发送方可能会悲观地认为也丢了(即有可能导致4和5的重传)。
发送方的两种选择:
仅仅重传timeout的包,节约宽带,但是需要继续等待后续报文ACK或者timeout重传(效率低)。
重传timeout后的所有包,浪费宽带,而且可能做了无用功。
(3)快速重传机制
如果发送方发出了1,2,3,4,5份数据,1先到了,于是就ack回2,结果2因为某些原因没收到。3到达了,ack也回2,后面的4和5都到了,ack也回2(2没有收到)。
于是发送端收到了三个ack=2的确认,知道了2还没有送达,于是重传2(或者2之后的所有包)。然后接收端收到了2,此时3,4,5都收到了,于是ack回6。
优点:不需要等待ACK超时,但是仍然没有解决是重传2还是重传2与之后的所有包。
(4)SACK 方法
SACK回复可以告知发送端需要排除哪些报文进行发送
该协议需要两边都支持。
强烈建议看看叔的文章
参考:https://coolshell.cn/articles/11564.html
https://www.cnblogs.com/linzhanfly/p/9968172.html
-深入原理-
知其然并知其所以然