重传

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,从而稳妥的探索可用带宽。 

    更多推荐