tcp的拥塞控制

tcp的拥塞控制

滑动窗口

滑动窗口的单位是字节。其值能够为MSS*N(mss的整数倍)算法

若是咱们在任一时间点对于这一过程作一个“快照”,那么咱们能够将TCP buffer中的数据分为如下四类,并把它们看做一个时间轴:缓存

  • 状态1 已发送已确认数据流中最先的字节已经发送并获得确认。这些数据是站在发送设备的角度来看的。以下图所示,31个字节已经发送并确认。
  • 状态2 已发送但还没有确认已发送但还没有获得确认的字节。发送方在确认以前,不认为这些数据已经被处理。下图所示14字节为第2类。
  • 状态3 未发送而接收方已Ready设备还没有将数据发出,但接收方根据最近一次关于发送方一次要发送多少字节确认本身有足够空间。发送方会当即尝试发送。如图,第3类有6字节。
  • 状态4 未发送而接收方Not Ready因为接收方not ready,还不容许将这部分数据发出。
    img

注意: 在tcp的一次报文传输过程当中,会发送多个字节,其做为一个总体,对端只会对此报文的最后一个字节进行ack,代表此报文的全部字节都接收成功。相应的,客户端也会将这些字节放入状态2,滑动窗口往前移动。服务器

拥塞窗口

实际的发送窗口,是min(cwnd, wnd).这就保证了网络

  • 发送的流量不会超过服务器缓存剩余的空间
  • 发送的流量不会超过当前网络链路的负荷

这里为了表述简单,咱们将拥塞窗口大小称为N,N的值为N mss字节.
tcp的拥塞控制主要有4个算法ssh

  • 慢启动
  • 拥塞避免
  • 快重传
  • 快恢复

ssthresh

关于慢启动的ssthresh值,有篇文章讲的比较好。TCP核心概念-慢启动,ssthresh,拥塞避免,公平性的真实含义,同时也剪藏在evernote中。tcp

  • 2N = rttr (r为发送的速率)
    N指什么呢?N是指字节数。当咱们站在上帝视角,咱们能清楚的知道这条链路的承载能力的,即这条TCP链路某一时刻承载的最大字节数。可是主机是不知道的,主机并不知道链接的TCP链路情况,所以主机要作的就是使ssthresh迫近N.
  • 2*N = wnd (N是肯定但未知的,而wnd是须要咱们不断迫近的)
    从理论上讲,wnd的值等于当前链路N*2,N=wnd/2.可是惋惜的是咱们不知道N,也就无从知道理论wnd是多少。咱们的目标就是找到理论wnd和N,在寻找的过程当中,咱们给N起个变量名: ssthresh. 这就清楚了,ssthresh就是咱们假定当前链路的承载能力。

疑问: 为何2*N = wnd
咱们假设当前的N为4,即:当前的TCP链路可承载4字节的信息。那么,咱们该将wnd设置为多大,才能充分利用此链路呢?
充分利用链路,就是说,此链路充满数据,服务器从接收一个,客户端发送一个,数据流不断
img
可见,在理想的状况下,当wnd=8的时候,客户端不用等待ack能够一直发送数据,不然客户端须要收到ack并将滑动窗口向前移动,才能发送新的数据。
注意: 在一个rtt中,共发送了wnd 字节的流量。spa


慢启动

初始化cwnd=1, 在每收到一个对新的报文段的确认后,把拥塞窗口增长至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可使分组注入到网络的速率更加合理。.net

当cwnd<ssthresh时,采用慢启动算法,当cwnd>ssthresh时,采用拥塞避免算法。
那么这个值具体的有什么讲究呢?这个在上面的连接中讲的比较清楚了,我把我本身的理解再简单的阐述一下。
ssthresh代表了当前网络链路中可容纳的最大字节数。当新建tcp连接的时候,并不清楚此tcp链路的情况,因此将sshthresh设置为一个巨大的值。而后每收到一个报文的ack,都将cwnd的值加1。所以,通过一个rtt, cwnd是呈指数级在增加。code

可是通常来讲cwnd是达不到初始 ssthresh的值的,在cwnd增加的过程当中,可能初始ssthresh的值远远大于链路的带宽,所以很快链路就会出现阻塞。注意这里区分两种状况:blog

  • 1 网络出现阻塞
  • 2 网络出现丢包,可是后续的报文被服务端成功收到。
    当网络出现阻塞的时候,须要进行以下操做:
  • ssthresh 减小为当前cwnd的一半
  • cwnd 设置为1 并开始新一轮的计算
    须要考虑一个问题,为何ssthresh须要设置为原来的一半?

当前的cwnd值过大,根本缘由是咱们假定当前链路的承载能力ssthresh过大,须要缩减,ssthresh = cwnd/2,就是说,在当前的cwnd状况下,其对应的ssthresh_new为 cwnd/2。所以咱们将ssthresh_old替换为ssthresh_new,即减为当前cwnd值得一半,并开始新一轮的迫近过程。
举个例子

理论状况:ssthresh=2 cwnd=4
初始化: ssthresh=5 cwnd=1
rtt1:  ssthresh 5 cwnd=1
rtt2:  ssthresh 5 cwnd=2(cwnd==ssthresh,改成拥塞避免算法)
rtt3:  ssthresh 5 cwnd=3
rtt4:  ssthresh 5 cwnd=4
rtt5:  ssthresh 5 cwnd=5
rtt6:  ssthresh 5 cwnd=6(可能开始出现阻塞丢包)
rtt7:  ssthresh 3 cwnd=1(reset)
...

疑问:那会不会存在cwnd/2>ssthresh_old的状况呢?这样不是ssthresh反而加大了?


拥塞避免

wnd = rtt*r
在一个rtt中,共发送了wnd 字节的流量。若是这wnd字节的流量都成功ack,那么咱们将cwnd+1
即:一个rtt 结束后,没有网络阻塞的话 ,cwnd+1。

能够很明显看到拥塞避免和慢启动的差别,慢启动时一个rtt后,cwnd增长一倍。

相关文章
相关标签/搜索