前面讲过,一条 TCP 连接的每一侧主机都为该连接设置了接收缓存。当该 TCP 连接收到正确、按序的字节后,它就将数据放入接收缓存。相关联的应用进程会从该缓存中读取数据,但不必是数据刚一到达就立即读取

    流量控制 - 图1

    事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才去读取该数据。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出。

    TCP 为它的应用程序提供了流量控制服务 (flow control service) 以消除发送方使接收方缓存溢岀的可能性。流量控制因此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。

    前面提到过,TCP 发送方也可能因为 IP 网络的拥塞而被遏制;这种形式的发送方的控制被称为拥塞控制 (congestion control)。

    即使流量控制和拥塞控制采取的动作非常相似 (对发送方的遏制),但是它们显然是针对完全不同的原因而采取的措施。现在我们来讨论 TCP 如何提供流量控制服务的。为了能从整体上看问题,我们在本节都假设 TCP 是这样实现的,即 TCP 接收方丢弃失序的报文段。

    TCP 通过让发送方维护一个称为接收窗口 (receive window) 的变量来提供流量控制。 通俗地说,接收窗口用于给发送方一个指示一一该接收方还有多少可用的缓存空间。因为 TCP 是全双工通信,在连接两端的发送方都各自维护一个接收窗口。我们在文件传输的情况下研究接收窗口。

    假设主机 A 通过一条 TCP 连接向主机 B 发送一个大文件。主机 B 为该连接分配了一个接收缓存,并用 RcvBuffer 来表示其大小。主机 B 上的应用进程不时地从该缓存中读取数据。我们定义以下变量:

    • LastByteRead:主机 B 上的应用进程从缓存读出的数据流的最后一个字节的编号。
    • LastByteRcvd:从网络中到达的并且已放入主机B接收缓存中的数据流的最后一个字节的编号。

    由于 TCP 不允许已分配的缓存溢岀,下式必须成立:

    流量控制 - 图2

    接收窗口用 rwnd 表示,根据缓存可用空间的数量来设置:

    流量控制 - 图3

    由于该空间是随着时间变化的,所以 rwnd 是动态的。下图对变量 rwnd 进行图示。

    接收窗口 rwnd

    连接是如何使用变量 rwnd 来提供流量控制服务的呢?主机 B 通过把当前的 rwnd 值放入它发给主机 A 的报文段接收窗口字段中,通知主机 A 它在该连接的缓存中还有多少可用空间。开始时,主机 B 设定rwnd = RcvBuffer。注意到为了实现这一点,主机 B 必须跟踪几个与连接有关的变量。

    主机 A 轮流跟踪两个变量,LastByteSent 和 LastByteAcked,这两个变量的意义很明显。 注意到这两个变量之间的差 LastByteSent - LastByteAcked 就是主机 A 发送到连接中但未被确认的数据量。通过将未确认的数据量控制在值 rwnd 以内,就可以保证主机 A 不会使主机 B 的接收缓存溢出。因此,主机 A 在该连接的整个生命周期须保证:

    流量控制 - 图5

    对于这个方案还存在一个小小的技术问题。为了理解这一点,假设主机 B 的接收缓存已经存满,使得 rwnd=0。在将 rwnd=0 通告给主机 A 之后,还要假设主机 B 没有任何数据要发给主机 A。

    此时,考虑会发生什么情况。因为主机 B 上的应用进程将缓存清空,TCP 并不向主机 A 发送带有 rwnd 新值的新报文段;事实上,TCP 仅当在它有数据或有确认要发时才会发送报文段给主机 A。

    这样,主机 A 不可能知道主机 B 的接收缓存已经有新的空间了,即主机 A 被阻塞而不能再发送数据!为了解决这个问题,TCP 规范中要求:当主机 B 的接收窗口为 0 时,主机 A 继续发送只有一个字节数据的报文段。这些报文段将会被接收方确认。最终缓存将开始清空,并且确认报文里将包含一个非 0 的 rwnd 值。

    流量控制 - 图6

    描述了 TCP 的流量控制服务以后,我们在此要简要地提一下 UDP 并不提供流量控制,报文段由于缓存溢出可能在接收方丢失。例如,考虑一下从主机 A 上的一个进程向主机 B 上的一个进程发送一系列 UDP 报文段的情形。

    对于一个典型的 UDP 实现,UDP 将在一个有限大小的缓存中加上报文段,该缓存在相应套接字(进程的门户)“之前”。进程每次从缓存中读取一个完整的报文段。如果进程从缓存中读取报文段的速度不够快,那么缓存将会溢出,并且将丢失报文段。

    作者开发的 Java 小程序,可以实现流量控制的模拟动画

    地址:https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/flow-control/index.html
    2021_12_16_1.gif