目录
- 1.流量控制
- 2.拥塞控制
- 0.为什么要有拥塞控制,不是有流量控制么?
- 1.什么是拥塞窗口?和发送窗口有什么关系呢?
- 2.怎么知道当前网络是否出现了拥塞呢?
- 3.拥塞控制有哪些算法?
- 4.慢启动
- 5.拥塞避免
- 6.拥塞发生
- 7.快速恢复
1.流量控制
-
发送方不能无脑的发数据给接收方,要考虑接收方处理能力
-
如果一直无脑地发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费
-
为了解决这种现象发生,TCP提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制
-
假设以下场景:客户端是接收方,服务端是发送方假设接收窗口和发送窗口相同,都为200,假设两个设备在整个传输过程中都保持相同的窗口大小,不受外界影响
-
根据下图的流量控制,说明下每个过程:
- 客户端向服务端发送请求数据报文
- **说明:**本次例子是把服务端作为发送方,所以没有画出服务端的接收窗口
- 服务端收到请求报文后,发送确认报⽂和80字节的数据,于是可用窗口Usable减少为120字节,同时SND.NXT指针也向右偏移80字节后,指向321,这意味着下次发送数据的时候,序列号是321
- 客户端收到80字节数据后,于是接收窗口往右移动80字节, RCV.NXT也就指向321,这意味着客户端期望的下⼀个报文的序列号是 321,接着发送确认报⽂给服务端
- 服务端再次发送了120字节数据,于是可用窗口耗尽为0,服务端无法再继续发送数据
- 客户端收到120字节的数据后,于是接收窗口往右移动120字节, RCV.NXT也就指向441,接着发送确认报文给服务端
- 服务端收到对80字节数据的确认报文后, SND.UNA指针往右偏移后指向321,于是可用窗口Usable增大到80
- 服务端收到对120字节数据的确认报文后, SND.UNA指针往右偏移后指向441,于是可用窗口Usable增大到200
- 服务端可以继续发送了,于是发送了160字节的数据后, SND.NXT指向601,于是可用窗口Usable减少到40
- 客户端收到160字节后,接收窗口往右移动了160字节, RCV.NXT也就是指向了601,接着发送确认报文给服务端
- 服务端收到对160字节数据的确认报文后,发送窗口往右移动了160字节,于是SND.UNA指针偏移了160后指向601,可用窗口Usable也就增大至了200
- 客户端向服务端发送请求数据报文
- 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端
- 窗口大小字段越大, 说明网络的吞吐量越高
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端
- 发送端接受到这个窗口之后,就会减慢自己的发送速度
- 如果接收端缓冲区满了, 就会将窗口置为0,这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端
2.拥塞控制
0.为什么要有拥塞控制,不是有流量控制么?
- 前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么
- ⼀般来说,计算机网络都处在⼀个共享的环境,因此也有可能会因为其他主机之间的通信使得网络拥堵
- 在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时TCP就会重传数据,但是⼀重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大
- 所以,TCP不能忽略网络上发生的事,它被设计成⼀个无私的协议,当网络发送拥塞时,TCP会自我牺牲,降低发送的数据量
- 于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络
- 为了在「发送方」调节所要发送数据的量,定义了⼀个叫做「拥塞窗口」的概念
1.什么是拥塞窗口?和发送窗口有什么关系呢?
- 拥塞窗口cwnd是发送方维护的⼀个的状态变化,它会根据网络的拥塞程度动态变化的
- 前面提到过发送窗口swnd和接收窗口rwnd是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是**swnd = min(cwnd, rwnd)**,也就是发送窗口和接收窗口中的最小值
- 拥塞窗口cwnd变化的规则
- 只要网络中没有出现拥塞,cwnd就会增大
- 但网络中出现了拥塞,cwnd就减少
2.怎么知道当前网络是否出现了拥塞呢?
- 只要「发送方」没有在规定时间内接收到ACK应答报文,也就是发生了超时重传,就会认为网络出现了拥塞
3.拥塞控制有哪些算法?
- 慢启动
- 拥塞避免
- 拥塞发生
- 快速恢复
4.慢启动
- TCP在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是⼀点⼀点的提高发送数据包的数量, 如果一上来就发大量的数据,这不就是给网络添堵吗?
- 慢启动的算法记住⼀个规则就行:当发送⽅每收到⼀个ACK,拥塞窗口cwnd的大小就会加1
- 这里假定拥塞窗口cwnd和发送窗口swnd相等,下面举个例子:
- 连接建立完成后,⼀开始初始化cwnd = 1,表示可以传⼀个MSS大小的数据
- 当收到⼀个ACK确认应答后,cwnd增加1,于是⼀次能够发送2个
- 当收到2个的ACK确认应答后, cwnd增加2,于是就可以比之前多发2个,所以这⼀次能够发送4个
- 当这4个的ACK确认到来的时候,每个确认cwnd增加1,4个确认cwnd增加4,于是就可以比之前多发4个,所以这⼀次能够发送8个
- 可以看出慢启动算法,发包的个数是指数性的增长
- 那慢启动涨到什么时候是个头呢?
- 有⼀个叫慢启动门限ssthresh(slow start threshold)状态变量
- 当cwnd < ssthresh时,使⽤「慢启动算法」
- 当cwnd >= ssthresh时,就会使⽤「拥塞避免算法」
5.拥塞避免
- 前⾯说到,当拥塞窗口cwnd「超过」慢启动门限ssthresh就会进入拥塞避免算法
- ⼀般来说ssthresh的大小是65535字节
- 进入拥塞避免算法后,它的规则是:每当收到⼀个 ACK 时,cwnd增加1/cwnd
- 接上前面的慢启动的例子,现假定ssthresh为8:
- 当8个ACK应答确认到来时,每个确认增加1/8,8个ACK确认cwnd⼀共增加1,于是下次能够发送9个MSS大小的数据,变成了线性增长
- 可以发现,拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,但是增长速度缓慢了⼀些
- 就这么⼀直增长着后,网络就会慢慢进⼊了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。当触发了重传机制,也就进入了「拥塞发生算法」
6.拥塞发生
-
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
- 超时重传
- 快速重传
-
发生超时重传的拥塞发生算法
- ssthresh和cwnd的值会发生变化:
- ssthresh设为cwnd/2
- cwnd重置为1
- 接着,就重新开始慢启动,慢启动是会突然减少数据流的
- ⼀旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿
- ⼀旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿
- ssthresh和cwnd的值会发生变化:
-
发生快速重传的拥塞发生算法
- 还有更好的方式,前⾯讲过「快速重传算法」。当接收⽅发现丢了⼀个中间包的时候,发送三次前⼀个包的ACK,于是发送端就会快速地重传,不必等待超时再重传
- TCP认为这种情况不严重,因为大部分没丢,只丢了⼀小部分,则ssthresh和cwnd变化如下:
- cwnd = cwnd/2 ,也就是设置为原来的⼀半
- ssthresh = cwnd
- 进⼊快速恢复算法
7.快速恢复
- 快速重传和快速恢复算法⼀般同时使用,快速恢复算法是认为,你还能收到3个重复ACK说明网络也不那么糟糕,所以没有必要像RTO超时那么强烈
- 正如前⾯所说,进⼊快速恢复之前, cwnd和ssthresh已被更新了
- cwnd = cwnd/2 ,也就是设置为原来的⼀半
- ssthresh = cwnd
- 然后,进入快速恢复算法如下
- 拥塞窗口**cwnd = ssthresh + 3**(3的意思是确认有3个数据包被收到了)
- 重传丢失的数据包
- 如果再收到重复的ACK,那么cwnd增加1
- 如果收到新数据的ACK后,把cwnd设置为第⼀步中的ssthresh的值,原因是该ACK确认了新的数据,说明从duplicated ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进⼊拥塞避免状态