5rfgn 发表于 2014-3-27 09:28:55

TCP丢包减窗和两大状态机

TCP丢包减窗和两大状态机TCP拥塞控制的核心在于丢包处理,窗口如何调节等。在Linux源码中,负责TCP拥塞控制核心的代码可以概括为两大状态机:TCP拥塞控制状态机和SACK标记重传状态机。前者尤为重要,负责TCP正常状态和拥塞状态的转换,后者负责标记丢包时的重传队列。本篇文章侧重TCP拥塞控制状态机的分析,两大状态机一起讲解会十分复杂,因此暂时不讨论SACK状态机。1.TCP拥塞控制状态机

其中,Disorder和Recovery状态是TCP丢包时进入的拥塞状态,十分关键。关于拥塞控制状态机的变换,请参阅Linux/net/ipv4/tcp_input.c, tcp_fastretrans_alert()函数。Disorder和Recovery两个状态最关键的窗口处理函数是tcp_moderate_cwnd和tcp_cwnd_down。Disorder状态基本不做窗口调整,滑动窗口相当于卡死状态。Recovery状态做降窗动作。    tcp_fastretrans_alert() {    …………………………………………………………………………………………………………………………………………………………………………      if (icsk->icsk_ca_state < TCP_CA_CWR) {      //disorder open < cwr      if (!(flag & FLAG_ECE))            tp->prior_ssthresh = tcp_current_ssthresh(sk);      tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk);               //这一句很关键,用当前选定的拥塞控制算法来调节阈值,多个拥塞控制算法可以切换,C语言实现多态。      TCP_ECN_queue_cwr(tp);    }    }    tcp_moderate_cwnd(struct tcp_sock *tp)    {//最多可以发送三个新的数据包,窗口基本不做调整,卡死状态      tp->snd_cwnd = min(tp->snd_cwnd,                   tcp_packets_in_flight(tp) + tcp_max_burst(tp));      tp->snd_cwnd_stamp = tcp_time_stamp;    }    tcp_cwnd_down(struct sock *sk, int flag)    {      struct tcp_sock *tp = tcp_sk(sk);      int decr = tp->snd_cwnd_cnt + 1;      //每收到两个ACK窗口减去1      if ((flag & (FLAG_ANY_PROGRESS | FLAG_DSACKING_ACK)) ||            (tcp_is_reno(tp) && !(flag & FLAG_NOT_DUP))) {            tp->snd_cwnd_cnt = decr & 1;            decr >>= 1;            if (decr && tp->snd_cwnd > tcp_cwnd_min(sk))                tp->snd_cwnd -= decr;            tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp) + 1);            tp->snd_cwnd_stamp = tcp_time_stamp;      }    }2.丢包时的窗口变化由于我未能看懂全部拥塞状态机处理的源码,这部分只是出于自己的理解,我在这部分也卡了很久,不得寸进,还望高手指点。
假设正常传输的cwnd为10,left表示窗口左侧,right表示窗口右侧,假设这时候1号包丢失:left_____________________________right      12345678910丢包发生后,窗口左侧卡死(因为没有收到新的ACK,窗口不能向右滑动,右侧也不能扩展),这是disorder状态,窗口不做调整,不发送新数据:left_____________________________right      12345678910重传1号包后,依次收到后续ACK: (进入Recovery状态,窗口降低)
收到1号包ACK后,窗口左侧移动,整体向右滑动,cwnd仍为大小10:left_______________________________right       234567891011收到2号包ACK,左侧向右滑动,cwnd减去1,遵循cwnd_down函数逻辑,cwnd变为9:      left___________________________right          34567891011    3号包ACK:         left____________________________right             4567891011124号包ACK:            left_________________________right                56789101112    5号包ACK:                left__________________________right                  6789101112136号包ACK:                   left_______________________right                     789101112137号包ACK:                      left________________________right                        8910111213148号包ACK:                         left_____________________right                           910111213149号包ACK:                            left______________________right                              10111213141510号包ACK:                              left__________________right                                    1112131415 如上,窗口降低是通过减慢窗口右端的滑动速度实现的,右侧的滑动速度是左侧的二分之一。 判断cwnd已经降低到了cwnd_loss/2,则降窗过程结束。 在丢包发生后,发送了cwnd_loss/2的新数据包进入网络。cwnd_loss是丢包发生时的窗口。ACK到达是很快的,一个RTT内就有一个整窗的ACK回复,因此,上面的减窗过程很快就完成了,相当于一个RTT内,窗口降低了二分之一。
以上分析了丢包时,TCP拥塞状态的处理。由于没有考虑SACK,以上的分析只是最简单的窗口调整模型,接近于TCP reno,如果考虑SACK,窗口处理会十分复杂。有兴趣者可以研究下。
这篇文章仍不完善,是为了解答某些专业人士的疑问而写出来,恐有错讹。最近很忙,短时期内不会再更新博客。
页: [1]
查看完整版本: TCP丢包减窗和两大状态机