目录
1、流量控制
2、TCP 的拥塞控制
(1)拥塞控制的原理
(2)拥塞控制的具体方法
1、流量控制
一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。
利用滑动窗口机制可以很方便地在 TCP 连接上实现对发送方的流量控制。
下面例子说明了如何利用滑动窗口机制进行流量控制:
假设 A 向 B 发送数据。在连接建立时,B 会告诉 A:“我的接收窗口 rwnd = 400。”(rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP 的窗口单位是字节,不是报文段。
我们注意到,上边图例的接收方 B 进行了三次流量控制。第一次把窗口减小到 rwnd = 300,第二次又减到 rwnd = 100,最后减到 rwnd =0,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机 B 重新发出一个新的窗口值为止。此外,还应注意到,B 向 A 发送的三个报文段都设置了 ACK=1,只有在 ACK=1 时确认号字段才有意义。//从这里,我们可以了解到流量控制需要了解接收方的状态,应由接收方掌握主动权
为什么流量控制中还需要使用到持续计时器呢?
试想一下,如果 B 向 A 发送了零窗口的报文段后不久,B 的接收缓存又有了一些存储空间。于是 B 向 A 发送了 rwnd = 400 的报文段。然而这个报文段在传送过程中丢失了,A 一直等待收到 B 发送的非零窗口的通知,而 B 也一直等待 A 发送的数据。如果没有其他措施,这种互相等待的死锁局面将一直延续下去。//等待死锁
因此,为了解决这个问题,TCP 为每一个连接设有一个持续计时器(persistence timer)。只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带 1 字节的数据),而对方就会在确认这个探测报文段时给出现在的窗口值。如果窗口仍然是零,那么收到这个报文段的一方就重新设置持续计时器。如果窗口不是零,那么死锁的僵局就可以打破了。//计时器的妙用
2、TCP 的拥塞控制
在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫作拥塞(congestion)。
可以把出现网络拥塞的条件写成如下的关系式:
对资源的需求 > 可用资源
若网络中有许多资源同时呈现供应不足,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。
那么,流量控制和拥塞控制有什么区别呢?
所谓拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至于过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。但 TCP 连接的端点只要迟迟不能收到对方的确认信息,就猜想在当前网络中的某处很可能发生了拥塞,但这时却无法知道拥塞到底发生在网络的何处,也无法知道发生拥塞的具体原因。(是访问某个服务器的通信量过大?还是在某个地区出现自然灾害?)//全局控制
相反,流量控制往往是指点对点通信量的控制,是个端到端的问题(接收端控制发送端)。流量控制所要做的就是抑制发送端发送数据的速率,以便接收端来得及接收。//端对端控制
流量控制和拥塞控制的区别可以用下图的简单比喻来说明,图中表示一水龙头通过管道向一个水桶放水。
图 A 表示水桶太小,来不及接收注入水桶的水。这时只好请求管水龙头的人把水龙头拧小些,以减缓放水的速率。这就相当于流量控制。
图 B 表示虽然水桶足够大,但管道中有很狭窄的地方,使得管道不通畅,水流被堵塞。这种情况被反馈到管水龙头的人,请求把水龙头拧小些,以减缓放水的速率,为的是减缓水管的堵塞状态。这就相当于拥塞控制。
虽然同样是把水龙头拧小些,但目的是很不一样的。
(1)拥塞控制的原理
弄清楚了流量控制和拥塞控制的区别后,我们应该如何进行拥塞控制呢?
下图中横坐标是提供的负载(offered load),代表单位时间内输入给网络的分组数目。因此提供的负载也称为输入负载或网络负载。纵坐标是吞吐量(throughput),代表单位时间内从网络输出的分组数目。//吞吐量越大,网络负载越大
具有理想拥塞控制的网络,在吞吐量饱和之前,网络吞吐量应等于提供的负载,故吞吐量曲线是 45° 的斜线。但当提供的负载超过某一限度时,由于网络资源受限,吞吐量不再增长而保持为水平线,即吞吐量达到饱和。这就表明提供的负载中有一部分损失掉了。虽然如此,在这种理想的拥塞控制作用下,网络的吞吐量仍然维持在其所能达到的最大值。
但是,实际网络的情况就很不相同了。从图中可看出,随着提供的负载的增大,网络吞吐量的增长速率逐渐减小。也就是说,在网络吞吐量还未达到饱和时,就已经有一部分的输入分组被丢弃了。当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态。更值得注意的是,当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了拥塞状态。当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作,这就是所谓的死锁(deadlock)。//数据全被堵在网络中了
从原理上讲,寻找拥塞控制的方案无非是寻找使不等式 "对资源的需求 > 可用资源" 不再成立的条件。这或者是增大网络的某些可用资源(如业务繁忙时增加一些链路,增大链路的带宽,或使额外的通信量从另外的通路分流),或减少一些用户对某些资源的需求(如拒绝接受新的建立连接的请求,或要求用户减轻其负荷,这属于降低服务质量)。
实践证明,拥塞控制是很难设计的,因为它是一个动态的(而不是静态的)问题。
由于计算机网络是一个很复杂的系统,因此可以从控制理论的角度来看拥塞控制这个问题。这样,从大的方面看,可以分为开环控制和闭环控制两种方法。
开环控制就是在设计网络时事先将发生拥塞的有关因素考虑周到,力求网络在工作时不产生拥塞。但一旦整个系统运行起来,就不再中途进行改正了。
闭环控制是基于反馈环路的概念,主要有以下几种措施:
- 监测网络系统以便检测到拥塞在何时、何处发生。
- 把拥塞发生的信息传送到可采取行动的地方。
- 调整网络系统的运行以解决出现的问题。
(2)拥塞控制的具体方法
TCP 进行拥塞控制的算法有四种,即慢开始(slow-start)、拥塞避免(congestion avoidance)、快重传(fast retransmit)和快恢复(fast recovery),下面将介绍这些算法的原理。
首先,让我们弄清楚什么是基于窗口的拥塞控制?
所谓基于窗口的拥塞控制,就是发送方会维持一个叫作拥塞窗口 cwnd (congestion window) 的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且是动态变化着的。发送方让自己的发送窗口等于拥塞窗口。这里假定对方的接收窗口足够大,发送方在发送数据时,只需考虑发送方的拥塞窗口。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。
那么,发送方又如何知道网络发生了拥塞呢?我们知道,当网络发生拥塞时,路由器就要把来不及处理而排不上队的分组丢弃。因此只要发送方没有按时收到对方的确认报文,也就是说,只要出现了超时,就可以估计可能在网络某处出现了拥塞。因此,发送方在超时重传计时器启动时,就判断网络出现了拥塞。
什么是慢开始算法?
慢开始算法的思路:当主机在已建立的 TCP 连接上开始发送数据时,并不清楚网络当前的负荷情况。如果立即把大量数据字节注入到网络,那么就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即由小到大逐渐增大注入到网络中的数据字节,也就是说,由小到大逐渐增大拥塞窗口数值。
需要注意的是,慢开始的“慢”并不是指 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时,只发送一个报文段,即设置 cwnd=1,目的是试探一下网络的拥塞情况,然后视情况再逐渐增大 cwnd。//慢开始算法中 cwnd 会加倍增长,比如 1,2,4,8,16...
什么是拥塞避免算法?
拥塞避免算法的目的是让拥塞窗口 cwnd 缓慢地增大。执行算法后的结果大约是这样的:每经过一个往返时间 RTT,发送方的拥塞窗口 cwnd 的大小就加 1,而不是像慢开始阶段那样加倍增长。因此在拥塞避免阶段就称为“加法增大”AI(Additive Increase),表明在拥塞避免阶段,拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
慢开始门限 ssthresh :
为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,一般设置一个慢开始门限 ssthresh 状态变量(可以把门限ssthresh的数值设置大些,例如达到发送窗口的最大容许值)。慢开始门限 ssthresh 的用法如下:
- 当 cwnd < ssthresh 时,使用上述的慢开始算法。
- 当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
- 当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。
下图曲线可以用来说明 TCP 的拥塞窗口 cwnd 是怎样随时间变化的:
当 TCP 连接已建立后,把拥塞窗口 cwnd 置为 1。上图中,慢开始门限的初始值设置为 16 个报文段,即 ssthresh = 16。在执行慢开始算法阶段,每经过一个往返时间 RTT,拥塞窗口 cwnd 就加倍。当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时,就改为执行拥塞避免算法,拥塞窗口按线性规律增长。但请注意,“拥塞避免”并非完全避免拥塞,而是让拥塞窗口增长得缓慢些,使网络不容易出现拥塞。
当拥塞窗口 cwnd = 24 时,网络出现了超时(图中的点②),这就是网络发生拥塞的标志。于是调整门限值 ssthresh = cwnd / 2= 12,同时设置拥塞窗口 cwnd = 1,执行慢开始算法。
什么是快重传算法?
当拥塞窗口 cwnd = 16 时(图中的点 4),出现了一个新的情况,就是发送方一连收到 3 个对同一个报文段的重复确认(图中记为 3-ACK)。
这个问题与网络不可靠有关。有时,个别报文段会在网络中意外丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,并误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口 cwnd 又设置为 1,因而不必要地降低了传输效率。//拥塞判断错误
快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。
如下图所示,接收方收到了 M1 和 M2 后都分别及时发出了确认:
现在接收方没有收到 M3 但却收到了 M4。本来接收方可以什么都不做。但按照快重传算法,接收方必须立即发送对 M2 的重复确认,以便让发送方及早知道接收方没有收到报文段 M3。发送方接着发送 M5 和 M6。接收方收到后也仍要再次分别发出对 M2 的重复确认。这样,发送方共收到了接收方的 4个对 M2 的确认,其中后 3 个都是重复确认。
快重传算法规定,发送方只要一连收到 3 个重复确认,就可知道现在并未出现网络拥塞,而只是接收方少收到一个报文段 M3,因而立即进行重传 M3 (即“快重传”)。
所以,采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失。
什么是快速恢复算法?
因此,在图的点 4,发送方知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。这时,发送方第 2 次调整门限值,使 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8 (见图中的点⑤),并开始执行拥塞避免算法。//恢复时直接跳过慢开始,进入拥塞避免算法
至此,全文结束。