滑动窗口的单位是字节。其值能够为MSS*N(mss的整数倍)算法
若是咱们在任一时间点对于这一过程作一个“快照”,那么咱们能够将TCP buffer中的数据分为如下四类,并把它们看做一个时间轴:缓存
注意: 在tcp的一次报文传输过程当中,会发送多个字节,其做为一个总体,对端只会对此报文的最后一个字节进行ack,代表此报文的全部字节都接收成功。相应的,客户端也会将这些字节放入状态2,滑动窗口往前移动。服务器
实际的发送窗口,是min(cwnd, wnd).这就保证了网络
- 发送的流量不会超过服务器缓存剩余的空间
- 发送的流量不会超过当前网络链路的负荷
这里为了表述简单,咱们将拥塞窗口大小称为N,N的值为N mss字节.
tcp的拥塞控制主要有4个算法ssh
关于慢启动的ssthresh值,有篇文章讲的比较好。TCP核心概念-慢启动,ssthresh,拥塞避免,公平性的真实含义,同时也剪藏在evernote中。tcp
疑问: 为何2*N = wnd
咱们假设当前的N为4,即:当前的TCP链路可承载4字节的信息。那么,咱们该将wnd设置为多大,才能充分利用此链路呢?
充分利用链路,就是说,此链路充满数据,服务器从接收一个,客户端发送一个,数据流不断。
可见,在理想的状况下,当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
当前的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增长一倍。