概述
1. TCP 重传机制
为保证数据安全到达接受端,TCP引入了超时重传、快速重传、SACK、D-SACK。
1.1 超时重传
以时间为基准,在发送数据时设置个定时器,如果期限内没收到接受者的ACK就会重新发送数据,一般数据包丢失或确认应答丢失会导致超时重传,这里先普及两个跟时间相关的参数跟一些规则。
- RTT:Round-Trip Time 往返时间,指的是数据从发送到接受的耗时时间。
- RTO :Retransmission Timeout 超时重传时间。
- 动态:RTT 收到网络波动是动态变化的,同理RTO也是动态变化的。
- RTO翻倍:每遇到一次超时重传,系统都会将下一次RTO翻倍。
RTT跟RTO之间的关系十分微妙。
- RTO 较小时可能导致数据本来就没丢失只是还没被响应, 又重发会增加网络拥塞,导致更多的超时重发。
- RTO较大时候可能导致数据已经丢了好久才重发数据。
所以离线情况下 RTO 稍微大于 RTT是最好的。具体规则有兴趣的可自行百度。
1.2 快速重传
TCP有累计确认机制,当接收端收到比期望序号大的报文段时,便会重复发送最近一次确认的报文段的确认信号,我们称之为冗余ACK(duplicate ACK)。
如图所示,报文段1成功接收并被确认ACK 2,接收端的期待序号为2,当报文段2丢失,报文段3失序到来,与接收端的期望不匹配,接收端重复发送冗余ACK 2。
发送端如果在超时重传定时器溢出之前,接收到连续的三个重复冗余ACK(其实是收到4个同样的ACK,第一个是正常的,后三个才是冗余的),发送端便知晓哪个报文段在传输过程中丢失了,于是重发该报文段,不需要等待超时重传定时器溢出,最后客户端收到 2,因为345已经回复过了,返回ACK6。
为啥是3次呢?
你要明白发送端即使按序发送,接收端也是会出现乱序的。乱序也会造成冗余ACK发送,那冗余ACK是乱序导致还是丢包导致呢?经过权衡把3次冗余ACK作为判定丢失的准则其本身就是估计值。
A为发送端,B为接收端,A的待发报文段序号为 【N-1,N,N+1,N+2】,假设报文段N-1成功到达。
- 在没丢失的情况下,有40%的可能出现3次冗余ACK,在乱序的情况下必定是2次冗余ACK。
- 在丢失的情况下,必定出现3次冗余ACK。
基于这样的概率,选定3次冗余ACK作为阈值也算是合理的。实际抓包时大多数的快速重传都会在大于3次冗余ACK后发生。
快速重传解决了超时问题,可是重传时是重传之前的一个,还是重传所有它是定不了的。
1.3 SACK
既然快速重传搞不定,就用 Selective Acknowledgment 选择性确认,原理也很简单,服务端给客户端回复的时候多加个字段SACK,SACK的内容就是告知发送端服务端收到了哪些。这样服务端可以根据收到的信息选择性发送丢失的包。
1.4 D-SACK
DSACK是在SACK的基础上做了一些扩展,主要用于对收到的重复报文进行了处理。DSACK同样使用了与SACK一样的报文格式。核心关注点是发送的时候出问题了还是回复的时候出问题了。
- 如果发送端发送数据A延时而触发了快速重传机制,快速重传机制发送过来的信息新A,然后老A又到了,接收端会回复SACK 意思是网络震荡导致的。
- 如果服务端的ACK 客户端没收到,客户端重发的时候,服务端会回复SACK,意思就是你的数据发送重复了
2. 滑动窗口
如果没有滑动窗口的机制:传输N份文件,就需要等待N次应答时间。
总的传输时间 = N份传输时间 + N份应答传输时间。
保证可靠性的前提下TCP 引入了窗口
概念,滑动窗口
可以让我们进一步提高传输效率。在窗口内的数据无需等待确认应答就可以继续发送数据。窗口的本质是OS开辟的一个缓存空间,然后进行批量传输,只要接收方没确认应答那么缓存中会一直存在。
总的传输时间 = N分数据传输时间叠加成一份时间,N份应答传输时间,重叠成一份时间
窗口大小为4000字节
窗口大小一般是接收方来决定的,接收方会告知发送方自己有多少缓存可接受数据,如果超过这个数据量接收方就无法接收了。
2.1 发送滑动窗口
在一的状态下发送方收到一个请求序列号2001的确认应答ACK,则2001前数据被标记为传输完毕,系统会进行窗口滑动变为二的样子。
- 窗口左边是已经发送并且受到服务器的ACK的数据,这些数据可以从缓存删除。
- 窗口内的数据其实也分为两类,一类是发送还没接收到ACK的,一类是还未发送的。在收到整个窗口的确认应答ACK之前,如果数据有丢失,发送端仍然需要重传。所以发送端需要有缓存保留可能被重传的数据,直到收到服务端ACK。
- 收到服务端ACK后,发送端会将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也别称为
滑动窗口控制
。 - 窗口模式下发送方也会根据接收方的能力来进行发送数据来进行
流量控制
。
2.2 窗口数据丢失
这里的数据丢失其实跟前面说到的重传机制类似,主要分为两种
(1)接收端收到信息但是返回ACK失败了:如果丢失ACK,不需要做任何处理,如3001这个ACK丢了,但是4001ACK却已经发送给主机A,说明2001~3000这个数据也顺利到达,3001ACK丢了无所谓,只要当前序号开始,就说明之前的数据已经正确传输到达主机B
(2)发送端发送数据中途数据丢失了:如下图1001-2000数据包丢了,而2001-3000,3001-4000都顺利到达,此时接收方反馈的ACK确认序号始终是1001,发送方如果发现接收方连续发送ACK都是1001,接收方就明白1001-2000这个数据丢包,就会重新传送,当接收方重新收到丢失的1001-2000数据后,直接返回ACK4001,因为2001-4000已经接受过放到缓存区了,接下来ACK直接从4001开始。
3. 拥塞控制
前面说到的流量控制只是单纯的对于发送方跟接受方而已,但是我们要知道网络一般都是公用的,别的服务器也可以能将网络搞阻塞,因为阻塞导致重发,然后重发导致更阻塞,最后陷入恶性循环。
为了控制发送方的数据量避免数据阻塞整个网络,发送方维护着一个叫拥塞窗口
的东西,前面说到过发送窗口
跟接受窗口
,现在由于有了拥塞窗口,此时 发送窗口swnd = min(拥塞窗口cwnd,接受窗口rwnd)。拥塞窗口的大小是动态变化的,当网络没阻塞就会变大,网络中有阻塞就会变小。判断阻塞的依据就是如果发送方在指定时间内没收到数据那就是阻塞了。
拥塞控制主要通过慢开始
,快重传
,快恢复
和避免拥塞
来实现的。
3.1 慢开始
TCP建立连接后系统有个慢启动的过程,意思就是一点一点的提高发送数据包的数量,慢启动的原则就是当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。有一个叫慢启动门限 slow start threshold 状态变量来充当最大值。
-
cwnd < ssthresh 时,使用慢启动算法。
-
cwnd >= ssthresh 时,使用拥塞避免算法。
3.2 拥塞避免算法
一般情况下 slow start threshold = 65535字节,系统进入拥塞避免算法后,每当收到一个 ACK 时,拥塞窗口就增加 1/拥塞窗口。拥塞避免算法存在的意义就是将慢开始的那种指数增长
变化为线程增长
。
3.3 快重传
进入拥塞避免算法后的数据随着不断增长最终会导致网络阻塞,最终引发丢包。然后会采用前面说到的超时重传
跟快速重传
。
-
超时重传:ssthresh = cwnd/2 同时 cwnd 重置为 1,然后重新开始慢启动,回到了起点。
-
快速重传:cwnd = cwnd/2 同时 ssthresh = cwnd 然后进入快速恢复算法。
3.4 快恢复
快恢复与快重传配合使用,当发送方接收到连续三个重复确认请求,为了避免网络拥塞,执行快速重传(cwnd = cwnd/2 同时 ssthresh = cwnd ),执行快速恢复算法。
-
cwnd = ssthresh + 3
-
重传丢失的数据包
-
收到重复ACK则 cwnd 累加 1。
-
收到新ACK后设置 cwnd = ssthresh,进入拥塞避免算法。
最后
以上就是执着紫菜为你收集整理的【网络编程】TCP原理进阶的全部内容,希望文章能够帮你解决【网络编程】TCP原理进阶所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复