计算机网络TCP拥塞控制窗口大小变化、重传、滑动窗口、流量控制等
转载自:[计算机网络] - TCP 重传、滑动窗口、流量控制、拥塞控制_3000~3500的数据早已被接收了,因为 ack 都到了 4000 了,已经意味着 4000 之前__浮生_的博客-CSDN博客
1. 重传机制
1.1 超时重传
重传机制的其中一个方式,就是在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK
确认应答报文,就会重发该数据,也就是我们常说的超时重传。
1.1.1 超时时间
我们先来了解一下什么是 RTT
(Round-Trip Time 往返时延)
RTT
就是数据从网络一端传送到另一端所需的时间,也就是包的往返时间。
超时重传时间是以 RTO
(Retransmission Timeout 超时重传时间)表示。
假设在重传的情况下,超时时间 RTO 「较长或较短」时,会发生什么事情呢?
- 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
- 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致
更多的超时,更多的超时导致更多的重发。
如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍。
也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?
1.2 快速重传
TCP 还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。
快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文
段。
快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传之前的一个,还是重传所有的问题。
比如对于上面的例子,是重传 Seq2 呢?还是重传 Seq2、Seq3、Seq4、Seq5 呢?因为发送端并不清楚这连续的三个 Ack 2 是谁传回来的。
根据 TCP 不同的实现,以上两种情况都是有可能的。可见,这是一把双刃剑。
为了解决不知道该重传哪些 TCP 报文,于是就有 SACK
方法。
1.3 SACK 方法
1.4 Duplicate SACK
Duplicate SACK 又称 D-SACK
,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
下面举例两个例子,来说明 D-SACK
的作用。
1.4.1 ACK 丢包
- 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时后,重传第一个数据
包(3000 ~ 3499) - 于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」
3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据
都已收到,所以这个 SACK 就代表着D-SACK
。 - 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了。
1.4.2 网络延时
- 数据包(1000~1499) 被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文。
- 而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的
数据包(1000~1499)又到了「接收方」; - 所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 DSACK,表示收到了重复的包。
- 这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而
是因为网络延迟了。
在 Linux 下可以通过 net.ipv4.tcp_dsack
参数开启/关闭这个功能(Linux 2.4 后默认打开)。
2.滑动窗口
2.1 引入窗口概念的原因
2.2 窗口大小
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。
2.3 发送方的滑动窗口
2.3.1 程序如何表示发送方的四个部分
TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(需要做偏移)。
SND.WND
:表示发送窗口的大小(大小是由接收方指定的);SND.UNA
:是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是
#2 的第一个字节。SND.NXT
:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3
的第一个字节。- 指向 #4 的第一个字节是个相对指针,它需要
SND.UNA
指针加上SND.WND
大小的偏移量,
就可以指向 #4 的第一个字节了。
那么可用窗口大小的计算就可以是:
可用窗口大 = SND.WND -(SND.NXT - SND.UNA)
2.4 接收方的滑动窗口
- #1 + #2 是已成功接收并确认的数据(等待应用进程读取);
- #3 是未收到数据但可以接收的数据;
- #4 未收到数据并不可以接收的数据;
其中三个接收部分,使用两个指针进行划分: RCV.WND
:表示接收窗口的大小,它会通告给发送方。RCV.NXT
:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的
第一个字节。- 指向 #4 的第一个字节是个相对指针,它需要
RCV.NXT
指针加上RCV.WND
大小的偏移量,
就可以指向 #4 的第一个字节了。
接收窗口和发送窗口的大小是相等的吗?
并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。
因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
3.流量控制
如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。
为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
3.1 操作系统缓冲区与滑动窗口的关系
前面的流量控制例子,我们假定了发送窗口和接收窗口是不变的,但是实际上,发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,而操作系统的缓冲区,会被操作系统调整。
当应用进程没办法及时读取缓冲区的内容时,也会对我们的缓冲区造成影响。
- 客户端作为发送方,服务端作为接收方,发送窗口和接收窗口初始大小为
360
; - 服务端非常的繁忙,当收到客户端的数据时,应用层不能及时读取数据。
可见最后窗口都收缩为 0 了,也就是发生了窗口关闭。当发送方可用窗口变为 0 时,发送方实际上会定时发送窗口探测报文,以便知道接收方的窗口是否发生了改变。
为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间在减少缓存,这样就可以避免了丢包情况。
3.2 窗口关闭
在前面我们都看到了,TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。
如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
3.2.1 窗口关闭潜在的危险
接收方向发送方通告窗口大小时,是通过 ACK
报文来通告的。
TCP 是如何解决窗口关闭时,潜在的死锁现象呢?
为了解决这个问题,TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。
如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
- 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器;
- 如果接收窗口不是 0,那么死锁的局面就可以被打破了。
窗口探查探测的次数一般为 3 次,每次次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST
报文来中断连接。
3.3 糊涂窗口综合症
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
要知道,我们的 TCP + IP 头有 40
个字节,为了传输那几个字节的数据,要搭上这么大的开销,这太不经济了。
3.3.1 让接收方不通告小窗口
当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0
,也就阻止了发送方再发数据过来。
等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
3.3.2 让发送方避免发送小数据
使用 Nagle 算法,该算法的思路是延时处理,它满足以下两个条件中的一条才可以发送数据:
只要没满足上面条件中的一条,发送方一直在囤积数据,直到满足上面的发送条件。
另外,Nagle 算法默认是打开的,如果对于一些需要小数据包交互的场景的程序,比如,telnet 或 ssh 这样的交互性比较强的程序,则需要关闭 Nagle 算法。
4. 拥塞控制
流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络中发生了什么。
一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。
所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。
于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。
为了在「发送方」调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念。
4.1 拥塞窗口
拥塞窗口 cwnd
是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化的。
那么怎么知道当前网络是否出现了拥塞呢?
其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。
4.2 拥塞控制算法
4.2.1 慢启动
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
时,就会使用「拥塞避免算法」。
4.2.2 拥塞避免算法
前面说道,当拥塞窗口 cwnd
「超过」慢启动门限 ssthresh
就会进入拥塞避免算法。
那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。
4.2.3 拥塞发生算法
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:
4.2.3.1 发生超时重传的拥塞发生算法
当发生了「超时重传」,则就会使用拥塞发生算法。
这个时候,sshresh 和 cwnd 的值会发生变化:
ssthresh
设为cwnd/2
,cwnd
重置为1
接着,就重新开始慢启动,慢启动是会突然减少数据流的。这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。
4.2.3.2 发生快速重传的拥塞发生算法
还有更好的方式,前面我们讲过「快速重传算法」。当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh
和 cwnd
变化如下:
cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;- 进入快速恢复算法
4.2.4 快速恢复算法
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO
超时那么强烈。
正如前面所说,进入快速恢复之前,cwnd
和 ssthresh
已被更新了:
- 拥塞窗口
cwnd = ssthresh + 3
( 3 的意思是确认有 3 个数据包被收到了) - 重传丢失的数据包
- 如果再收到重复的 ACK,那么 cwnd 增加 1
- 如果收到新数据的 ACK 后,设置 cwnd 为 ssthresh,接着就进入了拥塞避免算法
也就是没有像「超时重传」一夜回到解放前,而是还在比较高的值,后续呈线性增长。
计算机网络TCP拥塞控制窗口大小变化、重传、滑动窗口、流量控制等相关推荐
- TCP连续ARQ协议和滑动窗口协议
TCP协议通过使用连续ARQ协议和滑动窗口协议,来保证数据传输的正确性,从而提供可靠的传输. 一.ARQ协议 ARQ协议,即自动重传请求(Automatic Repeat-reQuest),是OSI模 ...
- 计算机网络-TCP拥塞控制
目录 1 拥塞概念 2 拥塞控制的一般原理 3 拥塞控制的方法 3.1 慢开始 (Slow start) 3.2 拥塞避免算法 3.3 快重传算法 3.4 快恢复算法 3.5 加法增大,乘法减小 (A ...
- tcp拥塞控制编程实验c语言代码,C语言 计算机网络TCP拥塞控制模拟程序
帮助你更好地认识TCP拥塞控制的机制 #include "stdio.h" #include "stdlib.h" void show() { //system ...
- tcp checksum incorrect_TCP 协议:滑动窗口
发送窗口/可用窗口: 第3部分是表示对方的接收窗口大小:第4部分是应用程序已调用write方法明确告知需要发送的 字节大小,但超过了接收方的可处理范围. 其他第3部分大小称为可用窗口,第2部分+第3部 ...
- 网络 TCP三次握手及滑动窗口
三次握手 客户端向服务器发出触发请求syn=1:因为这时还没有得到服务器的回应,所以ack=0 服务器接收到客户端的触发请求,回复ack=1,表示已经接收到客户端的请求:同时服务器也向客户端发出触发请 ...
- 计算机网络传输层(tcp滑动窗口与流量控制、拥塞控制)
④ TCP的滑动窗口 TCP的滑动窗口是以字节为单位的,是缓存的一部分,用来暂时存放字节流. 为了便于理解,我们只考虑A向B发送数据,B给出确认的场景.即A有发送窗口,B有接收窗口. 当发送方收到接收 ...
- TCP滑动窗口和拥塞控制机制
滑动窗口协议 滑动窗口协议(Sliding Window Protocol)属于TCP协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生.该协议允许发送方在停止并等待确认前发送多个数据分组 ...
- 网络(6)-TCP/IP对拥塞控制、滑动窗口如何实现可靠性?
一.拥塞控制 在拥塞控制上,采用广受好评的TCP拥塞控制算法(也称AIMD算法).该算法主要包括四个主要部分: 1.慢启动 每当建立一个TCP连接时或一个TCP连接发生超时重传后,该连接便进入慢启动阶 ...
- TCP/IP(十一)TCP滑动窗口和拥塞控制
目前建立在TCP协议上的网络协议特别多,有telnet,ssh,有ftp,有http等等.这些协议又可以根据数据吞吐量来大致分成两大类:(1)交互数据类型,例如telnet,ssh,这种类型的协议在大 ...
最新文章
- 简述PHP中有哪些运算符,PHP运算符简述
- JavaScript开发区块链只需200行代码
- java虚拟机的生命周期_深入理解Java虚拟机——JVM的生命周期
- svn强制要求提交注释
- python 获取当前文件夹下所有文件名
- pip install 报错UnicodeDecodeError: 'ascii' codec can't decode byte 0xb5 in
- 生产环境频繁内存溢出,原来就是因为这个“String类”
- 第二次课动手动脑的问题以及课后实验性的问题
- set 存放类或结构体的打印
- springboot+vue公众号页面授权获得微信openId
- box-shadow兼容IE8浏览器写法
- 解决报错:info There appears to be trouble with your network connection. Retrying...
- 链路追踪Zipkin
- Ubuntu 20.04 安装 Seismic Unix
- java 实现EME2000(国家大地坐标系)转ECEF坐标系(地心地固坐标系)
- poi实现word文档转pdf格式
- 名编辑电子杂志大师教程 | 如何删除电子画册中不要的页面?
- 会议室应用中的“三块屏”
- 《无极限之危情速递》观后感
- Canonical Juju 使用笔记
热门文章
- 密度聚类:OPTICS算法简单易懂版
- Ubuntu 设置多用户smba共享服务
- 超有用的前端配色网站
- 想做数码管显示,单片机IO口资源不够?看看WTV890语音芯片能做些啥
- 抢票软件依旧跑得欢 记者25分钟抢两张热门票
- 引领西装潮流文化的报喜鸟何以构建大国品牌
- linux编辑conf,Linux:我如何编辑resolv.conf
- 最全Pycharm教程(1)——定制外观
- Android Studio——Spinner 修改字体颜色和字体大小
- JAVA毕业设计飞机航班信息查询系统演示视频2021计算机源码+lw文档+系统+调试部署+数据库