拥塞窗口根据网络拥塞动态变化。每次未确认段时,都假定是由于网络拥塞。拥塞窗口随时间演变的方式被定义为一个算法,这取决于实现。我们现在将介绍最常见的一种。该算法遵循以下规则:
-
拥塞窗口从一个段的大小开始(大约 1KB)
-
定义了一个拥塞窗口阈值(ssthresh)
-
如果收到确认,并且当前拥塞窗口大小小于 ssthresh,则拥塞窗口加倍
-
如果收到确认,但拥塞窗口大于或等于 sshthresh,则拥塞窗口增加其初始值(例如 1KB)
-
如果一个段没有被确认从而触发重传,拥塞窗口就会减半并且 ssthresh 被放置在这个值
-
拥塞窗口不能大于接收器窗口
该规则中包括我们经常听过的几种算法:
-
慢启动(slow-start)
-
拥塞避免(congestion avoidance)
-
快速重传(fast retransmit)
-
快速恢复(fast recovery)
算法
–
通过下面的图片,可以方便大家更加快速的理解拥塞窗口的算法机制。
从上图中可以看出,每个设备都会跟踪自己的拥塞窗口(CWND,绿色)和对端的接收器窗口 (RWND)。
慢启动
当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。
每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间RTT。不过“传输轮次”更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。
另,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。
下面,我们从示例图着手,来分析慢启动的过程。
-
在协商连接时,两个设备交换它们的接收器窗口(在这种情况下它们都有 32KB)
-
双端都以 1KB 的拥塞窗口开始,但由于在该示例中客户端将是唯一发送数据的客户端,因此它将是唯一一个显着使用此值的客户端。
-
在第 2 行,客户端收到一个 ACK并将其 CWND 加倍(现在是 2k)
-
服务器在第 3 行收到一个ACK时也做同样的事情
-
客户端发送两段 1k 的数据,它们稍后在第 6 行和第 7 行确认,其中客户端上的拥塞窗口加倍(4k,然后是 8k)
-
然后,客户端再次发送 1k 数据并立即得到确认,有效地再次将拥塞窗口加倍(现在第 9 行为 16k)。
-
这在第 10-11 行重复,其中 CWND 达到 32k。
-
此时,除非接收端窗口也增长,否则拥塞窗口不能再增长。
慢启动的整个过程如下:
-
初始化 cwnd = 1
-
经过1个RTT,即收到一个ACK,cwnd = 2^(1) = 2
-
经过2个RTT, cwnd = 2^(2) = 4
-
经过3个 RTT, cwnd = 2^(3) = 8
拥塞避免
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
无论在慢启动阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢启动ssthresh设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。
❝
“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。“拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
❞
-
cwnd = i
-
经过 1 RTT, cwnd = i+1
-
2 RTT, cwnd = i+2
-
3 RTT, cwnd = i+3
快速重传
TCP 有一个快速传输特性——在它的计时器到期之前重新传输丢失的段。 为了允许快速传输,我们需要为发送方和接收方设置一些规则。
-
作为接收者,它应该始终发送它期望接收的序列号。 例如,当接收方接收到第 1 段时,它以 ACK2 响应,
-
作为发送方,它应该忽略定时器并在收到 3 个重复的ACK 后立即开始重传丢失的段。
用一句话概况,就是发送端在收到3个重复无序的ACK时候,它假定数据包丢失并重传该数据包,而无需等待重传计时器到期。
而在此时,拥塞窗口的变化过程如下:
-
ssthresh设置为拥塞窗口的1/2
-
拥塞窗口大小设置为ssthresh
-
重新进入拥塞避免阶段
快速恢复
-
当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。
-
每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组(如果新的cwnd允许发送)。
-
当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥塞避免,因为当分组丢失时我们将当前的速率减半。
算法
快速重传和快速恢复的目的是:快速恢复丢失的数据包。 如果没有快速重传和快速恢复这俩算法,那么tcp可能
Tahoe
Tahoe算法是TCP的早期版本。除了具备TCP的基本架构和功能外,引入了慢启动、拥塞避免以及快速重传机制。 在该算法中,快速重传机制策略如下:
-
ssthresh设置为拥塞窗口的1/2
-
拥塞窗口大小设置为1
-
重新进入慢启动阶段
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
我的面试宝典:一线互联网大厂Java核心面试题库
以下是我个人的一些做法,希望可以给各位提供一些帮助:
整理了很长一段时间,拿来复习面试刷题非常合适,其中包括了Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等,且还会持续的更新…可star一下!
283页的Java进阶核心pdf文档
Java部分:Java基础,集合,并发,多线程,JVM,设计模式
数据结构算法:Java算法,数据结构
开源框架部分:Spring,MyBatis,MVC,netty,tomcat
分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等
微服务部分:SpringBoot,SpringCloud,Dubbo,Docker
还有源码相关的阅读学习
源框架部分:Spring,MyBatis,MVC,netty,tomcat
分布式部分:架构设计,Redis缓存,Zookeeper,kafka,RabbitMQ,负载均衡等
微服务部分:SpringBoot,SpringCloud,Dubbo,Docker
[外链图片转存中…(img-8QU2EMnO-1711998524944)]
还有源码相关的阅读学习
[外链图片转存中…(img-EgYJUEya-1711998524944)]