TCP快速重传与快速恢复算法

 TCP快速重传与快速恢复算法

   在收到一个失序的报文段时,该报文段会被挂接到ooseg队列上,同时向发送端返回一个ACK(期待的下一个字节),很明显,这个ACK一定是个重复的ACK,且这个重复的ACK被发送出去的时候不会有任何延迟。接收端利用该重复的ACK,目的在于让对方知道收到一个失序的报文段,并告诉对方自己希望收到的序号。 但是在发送方看来,它不可能知道一个重复的ACK是由一个丢失的报文段引起的,还是由于仅仅出现了几个报文段的重新排序引起。因此我们需要等待少量重复的ACK到来。假如这只是一些报文段的重新排序,则在重新排序的报文段被处理并产生一个新的ACK之前,只可能产生1  ~  2个重复的ACK。如果一连串收到3个或3个以上的重复ACK,就非常可能是一个报文段丢失了。于是我们就重传丢失的数据报文段,而无需等待超时定时器溢出。这就是快速重传算法。

    当超时发生后,ssthresh 会被设置为有效发送窗口的一半,而cwnd被设置为一个报文段大小,即执行的是慢启动算法。而在这里,当执行完快速重传后,接下来执行的不是慢启动算法而是拥塞避免算法,这就是快速恢复算法。在快速重传后没有执行慢启动的原因在于,由于收到重复的  ACK 不仅仅告诉我们一个分组丢失了。而且由于接收方只有在收到另一个报文段,并将该报文段挂接到ooseg队列后,才会产生重复的ACK,这就说明,在收发两端之间仍然有流动的数据,而我们不想执行慢启动来突然减少数据流。


《TCPIP协议详解.卷1》中描述的该算法步骤如下:
1) 当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。
2) 每次收到另一个重复的  ACK 时,cwnd增加1个报文段大小并发送  1 个分组(如果新的cwnd允许发送)。
3) 当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤  1 中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。


        LWIP 也是在函数tcp_receive 中实现快速恢复与重传的,如下所示,整个过程与上面算法所述基本相同。
      if (pcb->lastack == ackno) {      //  如果该ACK是个重复的ACK
          pcb->acked = 0;        //  则被该ACK确认的数据个数为0
          if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){ // 如果᳾进行窗口更新
                ++pcb->dupacks;    //  收到重复确认的次数加1
                if (pcb->dupacks >= 3 && pcb->unacked != NULL) { //如1)所述,三个以上充复ACK
                    if (!(pcb->flags & TF_INFR)) { //  此时快速重传᳾开启,即dupacks为3次
                        tcp_rexmit(pcb);      //  调用函数重传丢失的报文段
                        if (pcb->cwnd > pcb->snd_wnd)              // ssthresh设置为有效发送窗口的一半
                              pcb->ssthresh = pcb->snd_wnd / 2;
                        else
                              pcb->ssthresh = pcb->cwnd / 2;
                        if (pcb->ssthresh < 2*pcb->mss) {      //  修正ssthresh值,最小为2个报文段
                            pcb->ssthresh = 2*pcb->mss;
                        }
                                                                                                                                    
E-mail:[email protected]                                                                                                       
                        pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;// cwnd为ssthresh+3*报文段大小
                        pcb->flags |= TF_INFR;      //  设置快速重传标志
              }  
else    // 快速重传已经开始,即dupacks大于3次
{
                      if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd)    // 快速重传已经开始,如2)
                              pcb->cwnd += pcb->mss; //  每收到一个重复ACK,cwnd增加1个报文段大
              }                  //这与2)中描述的有区别,这里收到重复ACK后没有发送  1 个分组
        } // if dupacks 大于3
    } //if  如果᳾进行窗口更新
}      // if 如果是重复的ACK
else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_max)){ //确认了新的数据
              if (pcb->flags & TF_INFR) {        //  正处于快速重传状态,
                  pcb->flags &= ~TF_INFR;        //  清除快速重传标志
                  pcb->cwnd = pcb->ssthresh;      //  如3)所示,设置cwnd的值
                }
            …….
            pcb->dupacks = 0;      // 清除重复确认标志
            pcb->lastack = ackno; //  记录ackno
            …….
}

猜你喜欢

转载自blog.csdn.net/vincent_yuan89/article/details/9494113
今日推荐