【TCP系列】重传、滑动窗口、流量控制、拥塞控制
重传
1、超时重传
- 数据包丢失
- 确认应答丢失
2、快重传
快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。
快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传一个,还是重传所有的问题
3、选择性确认重传
还有一种实现重传机制的方式叫:SACK( Selective Acknowledgment), 选择性确认。
这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将已收到的数据的信息发送给「发送方」,
这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能
滑动窗口
滑动窗口是 TCP 协议中的一个重要概念,它用于控制数据包的发送和接收速率,从而实现网络拥塞控制和数据包丢失的最小化。
1、窗口大小
指无需等待确认应答,而可以继续发送数据的最大值。
窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。
TCP 头里有一个字段叫 Window,也就是窗口大小。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
通常窗口的大小是由接收方的窗口大小来决定的。
发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。
2、发送方的滑动窗口
下图就是发送方缓存的数据,根据处理的情况分成四个部分,其中深蓝色方框是发送窗口,紫色方框是可用窗口:

- #1 是已发送并收到 ACK确认的数据:1~31 字节
- #2 是已发送但未收到 ACK确认的数据:32~45 字节
- #3 是未发送但总大小在接收方处理范围内(接收方还有空间):46~51字节
- #4 是未发送但总大小超过接收方处理范围(接收方没有空间):52字节以后
3、接收方的滑动窗口
接收窗口相对简单一些,根据处理的情况划分成三个部分:
- #1 + #2 是已成功接收并确认的数据(等待应用进程读取);
- #3 是未收到数据但可以接收的数据;
- #4 未收到数据并不可以接收的数据;

4、接收窗口和发送窗口的大小是相等的吗?
并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。
因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
流量控制
发送方不能无脑的发数据给接收方,要考虑接收方处理能力。
如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。
为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
接收端将自己可以接收的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK报文来通知发送端,接收端用来控制发送端发送数据的大小,从而达到流量控制。
- 滑动窗口:控制的是接收方,怕发送方发的太快,接收方来不及处理
- 拥塞窗口:控制的是网络,怕发送方发的太快,造成网络拥塞,使得网络来不及处理
拥塞控制
拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络
拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。
拥塞窗口 cwnd 变化的规则:
- 只要网络中没有出现拥塞,cwnd 就会增大;以便把更多分组发送出去,提高网络利用率。
- 但网络中出现了拥塞,cwnd 就减少;减少注入到网络中的分组,以便缓解网络出现拥塞。
那么怎么知道当前网络是否出现了拥塞呢?
其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。
拥塞控制主要是四个算法:
- 慢启动
- 拥塞避免
- 拥塞发生
- 快速恢复

1、慢启动
TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提高发送数据包的数量,如果一上来就发大量的数据,这不是给网络添堵吗。
慢启动的算法记住一个规则就行:
初始设置一个较小的值,比如发送方的最大报文段大小(MSS);当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。
慢启动算法,发包的个数是指数性的增长。
那慢启动涨到什么时候是个头呢?
- 有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
- 当 cwnd < ssthresh 时,使用慢启动算法。
- 当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
2、拥塞避免
当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。
一般来说 ssthresh 的大小是 65535 字节。
拥塞避免算法的规则是:每当一个RTT(往返时间)成功传输所有数据并收到 ACK 时,cwnd 就增加一个MSS。
拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了一些。
就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。
当触发了重传机制,也就进入了「拥塞发生算法」。
3、拥塞发生
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
- 超时重传
- 快速重传
快重传算法规则:如果发送方收到三个重复的ack,立即重传丢失的数据包。
当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:
- cwnd = cwnd/2 ,也就是设置为原来的一半;
- ssthresh = cwnd;
进入快速恢复算法
4、快速恢复
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
正如前面所说,进入快速恢复之前,cwnd 和 ssthresh 已被更新了:
- cwnd = cwnd/2 ,也就是设置为原来的一半;
- ssthresh = cwnd;
然后,进入快速恢复算法如下:
- 拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
- 重传丢失的数据包;
- 如果再收到重复的 ACK,那么 cwnd 增加 1;
- 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;
快恢复算法规则:在接收到3个重复ack后,ssthresh设置为cwnd的一半,然后cwnd设置为ssthresh+3个MSS,这样做是为了反映在网络中还有传输的数据包,随后,对于接收到的每个重复ack,cwnd就递增一个MSS,当收到新ack时,将cwnd设置为ssthresh的值。
快速恢复是拥塞发生后慢启动的优化,其首要目的仍然是降低 cwnd 来减缓拥塞,所以必然会出现 cwnd 从大到小的改变。
“乘法减小”:当检测到拥塞(超时或接收到重复ack),乘法减小策略会将ssthresh和cwnd减半,以减少网络中的数据流量。
“加法增大”:在拥塞避免阶段,当网络表现出稳定性时,使用加法增大策略逐步增加cwnd,从而稳妥的探索可用带宽。
更多推荐


所有评论(0)