《Linux高性能服务器编程》——3.10 拥塞控制

简介: 本节书摘来自华章计算机《Linux高性能服务器编程》一书中的第3章,第3.10节,作者 游双,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.10 拥塞控制

3.10.1 拥塞控制概述

TCP模块还有一个重要的任务,就是提高网络利用率,降低丢包率,并保证网络资源对每条数据流的公平性。这就是所谓的拥塞控制。

TCP拥塞控制的标准文档是RFC 5681,其中详细介绍了拥塞控制的四个部分:慢启动(slow start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)和快速恢复(fast recovery)。拥塞控制算法在Linux下有多种实现,比如reno算法、vegas算法和cubic算法等。它们或者部分或者全部实现了上述四个部分。/proc/sys/net/ipv4/tcp_congestion_control文件指示机器当前所使用的拥塞控制算法。

拥塞控制的最终受控变量是发送端向网络一次连续写入(收到其中第一个数据的确认之前)的数据量,我们称为SWND(Send Window,发送窗口)。不过,发送端最终以TCP报文段来发送数据,所以SWND限定了发送端能连续发送的TCP报文段数量。这些TCP报文段的最大长度(仅指数据部分)称为SMSS(Sender Maximum Segment Size,发送者最大段大小),其值一般等于MSS。

发送端需要合理地选择SWND的大小。如果SWND太小,会引起明显的网络延迟;反之,如果SWND太大,则容易导致网络拥塞。前文提到,接收方可通过其接收通告窗口(RWND)来控制发送端的SWND。但这显然不够,所以发送端引入了一个称为拥塞窗口(Congestion Window,CWND)的状态变量。实际的SWND值是RWND和CWND中的较小者。图3-11显示了拥塞控制的输入和输出(可见,它是一个闭环反馈控制)。

image

3.10.2 慢启动和拥塞避免

TCP连接建立好之后,CWND将被设置成初始值IW(Initial Window),其大小为2~4个SMSS。但新的Linux内核提高了该初始值,以减小传输滞后。此时发送端最多能发送IW字节的数据。此后发送端每收到接收端的一个确认,其CWND就按照式(3-1)增加:

CWND+=min(N,SMSS)            (3-1)

其中N是此次确认中包含的之前未被确认的字节数。这样一来,CWND将按照指数形式扩大,这就是所谓的慢启动。慢启动算法的理由是,TCP模块刚开始发送数据时并不知道网络的实际情况,需要用一种试探的方式平滑地增加CWND的大小。

但是如果不施加其他手段,慢启动必然使得CWND很快膨胀(可见慢启动其实不慢)并最终导致网络拥塞。因此TCP拥塞控制中定义了另一个重要的状态变量:慢启动门限(slow start threshold size,ssthresh)。当CWND的大小超过该值时,TCP拥塞控制将进入拥塞避免阶段。

拥塞避免算法使得CWND按照线性方式增加,从而减缓其扩大。RFC 5681中提到了如下两种实现方式:

image

CWND+=SMSS*SMSS/CWND            (3-2)

图3-12粗略地描述了慢启动和拥塞避免发生的时机和区别。该图中,我们以SMSS为单位来显示CWND(实际上它是以字节为单位的),以次数为单位来显示RTT,这只是为了方便讨论问题。此外,我们假设当前的ssthresh是16SMSS大小(当然,实际的ssthresh显然远不止这么大)。

image

以上我们讨论了发送端在未检测到拥塞时所采用的积极避免拥塞的方法。接下来介绍拥塞发生时(可能发生在慢启动阶段或者拥塞避免阶段)拥塞控制的行为。不过我们先要搞清楚发送端是如何判断拥塞已经发生的。发送端判断拥塞发生的依据有如下两个:

image

拥塞控制对这两种情况有不同的处理方式。对第一种情况仍然使用慢启动和拥塞避免。对第二种情况则使用快速重传和快速恢复(如果是真的发生拥塞的话),这种情况将在后面讨论。注意,第二种情况如果发生在重传定时器溢出之后,则也被拥塞控制当成第一种情况来对待。

如果发送端检测到拥塞发生是由于传输超时,即上述第一种情况,那么它将执行重传并做如下调整:

ssthresh=max(FlightSize/2,2*SMSS)          (3-3)
CWMD<=SMSS

其中FlightSize是已经发送但未收到确认的字节数。这样调整之后,CWMD将小于SMSS,那么也必然小于新的慢启动门限值ssthresh(因为根据式(3-3),它一定不小于SMSS的2倍),故而拥塞控制再次进入慢启动阶段。

3.10.3 快速重传和快速恢复

在很多情况下,发送端都可能接收到重复的确认报文段,比如TCP报文段丢失,或者接收端收到乱序TCP报文段并重排之等。拥塞控制算法需要判断当收到重复的确认报文段时,网络是否真的发生了拥塞,或者说TCP报文段是否真的丢失了。具体做法是:发送端如果连续收到3个重复的确认报文段,就认为是拥塞发生了。然后它启用快速重传和快速恢复算法来处理拥塞,过程如下:

1)当收到第3个重复的确认报文段时,按照式(3-3)计算ssthresh,然后立即重传丢失的报文段,并按照式(3-4)设置CWND。

CWND=ssthresh+3*SMSS             (3-4)

2)每次收到1个重复的确认时,设置CWND=CWND+SMSS。此时发送端可以发送新的TCP报文段(如果新的CWND允许的话)。

3)当收到新数据的确认时,设置CWND=ssthresh(ssthresh是新的慢启动门限值,由第一步计算得到)。

快速重传和快速恢复完成之后,拥塞控制将恢复到拥塞避免阶段,这一点由第3步操作可得知。

相关文章
|
7月前
|
安全 算法 Ubuntu
Linux(openssl)环境:编程控制让证书自签的技巧。
总结:在Linux环境中,OpenSSL是一个非常实用的工具,可以帮助我们轻松地生成自签名证书。通过上述三个简单步骤,即可为内部网络、测试环境或开发环境创建自签名证书。但在公共访问场景下,建议购买经过权威认证机构签发的证书,以避免安全警告。
338 13
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
9月前
|
JavaScript Ubuntu Linux
如何在阿里云的linux上搭建Node.js编程环境?
本指南介绍如何在阿里云Linux服务器(Ubuntu/CentOS)上搭建Node.js环境,包含两种安装方式:包管理器快速安装和NVM多版本管理。同时覆盖全局npm工具配置、应用部署示例(如Express服务)、PM2持久化运行、阿里云安全组设置及外部访问验证等步骤,助你完成开发与生产环境的搭建。
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
209 26
|
10月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
211 17
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
549 13
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
735 6
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
401 3