计算机网络学习笔记 - 第六章

第 6 章 传输层

6.1 传输服务

6.1.1 提供给上层的服务

传输层的功能

设置传输层的原因

传输层提供的服务

6.1.2 传输服务原语

段、包、帧

段/TPDU

使用术语段 (segment) 来表示传输实体之间发送的消息。一些老协议使用了更加笨拙的名称一传输协议数据单元 (TPDU, Transport Protocol Data Unit)

6.1.3 Berkeley 套接字

TCP 套接字原语

6.1.4 套接字编程实例:Internet 文件服务器

套接字编程实例

6.2 传输协议的要素

传输层协议和数据链路层协议的比较

由于传输层上存在着大量并且数量可变的连接,而且由于连接之间的相互争造成连接的可用带宽上下波动

6.2.1 寻址

TSAP

使用通用术语传输服务访问点 (TSAP, Transport Service Access Point) 来表示传输层的一个特殊端点

NSAP

网络层上的类似端点(即网络层地址)毫不奇怪地称为网络服务访问点 (NSAP, Network Service Access Point)。

TSAP 和 NSAP

获取 TSAP 地址

具有固定 TSAP 地址的服务被列出在一些知名的文件中,比如 UNIX 系统上的/etc/services 文件, 该文件列出了哪些服务器被永久地关联到哪些端口上

进程服务器

一台机器上可能存在多个服务器进程,其中有许多服务很少被人使用。如果让这些服务器进程一直都活跃着,并整天监听一个稳定的 TSAP 地址,则是一种浪费。

不是每个想象的服务器都在一个知名 TSAP 上监听,每台希望向远程用户提供服务的机器有个特殊的进程服务器 (process server) 充当那些不那么频繁使用的服务器的代理。

6.2.2 连接建立

连接建立的问题

初看起来,好像一个传输实体只要给接收方发送一个 CONNECTION REQUEST 段,然后等待 CONNECTION ACCEPTED 应答就足够了。当网络可能出现丢失、延退、损坏和重复数据包的时候,问题就发生了。这些行为将导致极为严重的复杂性。

一种方法是使用一次性的传输地址。

另一种可能的做法是连接发起方为每个连接分配一个唯一标识符(即一个序号,每建立一个连接,该序号就递增),并且将该标识符放在每个段中,也包括请求建立连接的那个段。 如果一台机器崩溃丢失了它的全部内存,那么它就不可能知道哪些连接标识符已经被对等实体用过了。

解决超时重复数据包的问题

方法的核心是源端用序号作为段的标签,使得该段在 T 秒内不被重用。T 的大小以及数据包速率(数据包/秒)确定了序号的大小。这样,在任何给定的时间内只能出现一个给定序号的数据包。这个数据包的重复现象仍然有可能发生,但是它们必须被接收方丢弃。然而,这样的情况不会再发生:即一个具有相同序号的重复的老数据包击败一个新数据包,取而代之地被接收方接受。

为了解决一台机器崩溃之后丢失所有内存的问题,一种可能的解决方法是要求传输实体在机器崩溃之后空闲 T 秒。这段空闲期将确保所有老的段全部死掉,因而发送端可以启用任何一个序号值。

每台主机都配备一个日时钟 (time-of-day clock)。不同主机上的时钟不需要同步。假定每个时钟均采用了二进制计数器的形式。该计数器以统一的时间间隔递增自己,而且,计数器的位数必须等于或者超过序号的位数。最后,也是最重要的假设是,即使主机停机时钟也不停下,它将持续不停地运行。当建立一个连接时,时钟的低 k 位被用于同样是 k 位的初始序号。

三次握手 (three-way handshake)

三次握手 (three-way handshake) 这是一个建立连接采用的协议,它要求一方检査连接请求是否的确是当前的

主机 1 选择一个序号 x,并且发送一个包含 x 的 CONNECTION EQUEST 段给主机 2。主机 2 回应一个 ACK 段作为对 x 的确认,并且宣告它自己的初始序号 y。最后,主机 1 在它发送的第一个数据段中,对主机 2 选择的初始序号进行确认。

6.2.3 连接释放

非对称释放/对称释放

非对称释放连接是电话系统的工作方式:当一方挂机后,连接就被中断了。对称释放连接是把连接看成两个独立的单向连接,要求单独释放每个单向连接。

非对称释放方法太冒失,可能导致数据的丢失

两军对垒问题

连接的释放

释放一个连接意味着传输实体将有关该连接的信息从它的内部表(记录了所有当前已打开的连接)中删除,并且通知该连接的所有者(传输用户)。这个动作与传输用户发出一个 DISCONNECT 原语有所不同

半开连接 (half-open)

虽然这个协议在一般情况下已经足够了,但在理论上,如果初始的 DR 和 N 次重传全部丢失,则协议可能失败。发送端将最终放弃发送释放连接请求并强行释放连接,而另方对所有的释放连接学试无所知,它的连接仍然处于活跃状态。这种情况会导致一个半开的 (half-open) 连接。

杀死半开连接可以采用这样一条规则:如果在规定的一段时间内没有段到来,该连接将被自动断开。

6.2.4 差错控制和流量控制

传统差错控制方法
  1. 帧中携带一个检错码(比如,CRC 或者校验和)用于检测信息是否被正确接收

  2. 帧中携带的序号用于标识本帧,发送方在收到接收方成功接收后返回的确认之前,必须重发帧。这种机制称为自动重复请求 (ARQ, Automatic Repeat request)

  3. 任何时候允许发送方发送一定数量的帧,如果接收方没有及时返回确认,则发送方必须暂停。如果只允许发送一帧,则协议称为停等式 (stop-and-wait) 协议。较大的窗口可使得发送管道化,因而提高距离长且速度快的链路性能。

  4. 滑动窗口 (sliding window) 协议结合了这些功能,还能被用于支持数据的双向传送。

端到端的传输层校验

链路层校验和只保护在一条链路上经过的数据包,而没有考虑它们在路由器内部出错的情况。因此,即使根据每条链路的校验和检査都正确,数据包仍然会被不正确地传递

如何组织缓冲池

如果大多数段的长度都差不多,很自然的做法是将缓冲区组织成一个由大小统一的缓冲区构成的池,每个缓冲区容纳一个段

另一个方法是使用可变大小的缓冲区

第三种可能的方案是为每个连接使用一个大的循环缓冲区

动态缓冲区分配

随着连接被打开和关闭,以及流量模式的变化,发送端和接收端需要动态地调整它们的缓冲区分配策略。

接收端在逆向流量中捎带上单独的确认和缓冲区数。TCP 采用这种模式,将缓冲区的分配捎带在头的 Window size 字段中

例:动态缓冲区分配

为了避免出现这种情况,每台主机应该定期地在每个连接上发送控制段,这些控制段给出确认和缓冲区状态。采用这种方法,死锁迟早会被打破。

流量控制

当缓冲区空间不再限制最大数据流时,另一个"瓶颈"将会出现:网络的承载能力。

发送端动态地调整窗口的大小,以便与网络的承载容量相匹配。这意味着动态滑动窗口可以同时实现流量控制和拥塞控制。

6.2.5 多路复用

多路复用 (multiplexing)/逆向多路复用 (inverse multiplexing)

当到达了一个段,必须有某种方式告知把它交给哪个进程处理。这种情况,称为多路复用 (multiplexing),

假设一台主机有多条网络路径可用。如果用户需要的带宽和可靠性比其中任何一条路径所能提供的还要多,那么一种解决办法是以轮询的方式把一个连接上的流量分到多条网络路径,如图 6-17(b) 所示。这种手法称为逆向多路复用 (inverse multiplexing)

6.3 拥塞控制

拥塞控制的途径

拥塞发生在路由器上,因此在网络层检测拥塞。然而,拥塞究竟还是由传输层注入到网络中的流量引起的,因此控制拥塞的唯一途径是传输层放缓往网络中发送数据包的速度。

6.3.1 理想的带宽分配

拥塞控制算法的目标

拥塞控制算法的目标是更加易于避免拥塞,即为使用网络的传输层找到一种好的带宽分配方法。 个良好的带宽分配方法能带来良好的性能,因为它能利用所有的可用带宽却能避免拥塞, 而且它对于整个竟争的传输实体是公平的,并能快速跟踪流量需求的变化。

衡量拥塞程度的指标

功率=负载/延迟

功率最初将随着提交负载的上升而上升,延退仍然很小并且基本保持不变;但随着延退快速增长功率将达到最大,然后开始下降。达到最大功率的负载表示了传输实体放置在网络上的有效负载。

最大-最小公平

公平的形式是最大-最小公平 (max-min fairness),通常表示理想的网络使用情况。最大最小公平分配指的是,如果分配给一个流的带宽在不减少分配给另一个流带宽的前提下无法得到进一步增长,那么就不给这个流更多带宽。也就是说,增加一个流的带宽只会让不太富裕的那些流的情况变得更糟。

一种直观的思考方式是想象所有的流从速率零开始,然后缓慢地增加速率。当任何一个流的速率遇到瓶颈,就停止该流的速率增加;所有其他的流继续加各自的速率,平等共享可用容量,直到它们也达到各自的"瓶颈"。

基本含义:使得资源分配向量的最小分量的值最大,防止任何网络流被'饿死',同时在一定程度上尽可能增加每个流的速率。

原则:一个信道上,在满足最小需求的前提下,各流尽量均分带宽;

如果增加任何一个参与者(分量)的带宽,将导致其它的具有相同或者更少带宽的参与者(分量)的带宽下降,此时即满足了最大最小公平性。

收敛性 (convergence)

由于需求的变化,网络的理想操作点也随着时间推移而改变着。一个良好的拥塞控制算法,应迅速收敛到理想的操作点,并跟踪这随时变化的操作点。

6.3.2 调整发送速率

流量控制和拥塞控制

一些拥塞控制机制

如何控制发送速率

如果给出一个显式的和精确的信号,那么传输实体就可以利用该信号来调整发送速率到新的操作点。

在其他情况下,涉及一些猜测工作。在没有拥塞信号的情況下,发送端应该增加发送速率当给出拥塞信号时,发送端应降低发送速率。增加或减少速率的方式由控制法则 (control law) 决定。

AIAD 和 MIMD

在这里有两个流竞争单条链路的带宽。在图 6-24 中,分配给用户 1 的带宽用 x 轴表示,分配给用户 2 的带宽用 y 轴表示。当分配满足公平性时,两个用户将获得相同数量的带宽。这在图中用一条虚的公平线表示。当分配的带宽总和为 100%时,即达到链路容量时分配是有效的,这在图中用一条虚线效率线表示。当两个用户分配的带宽总和超过这条线时,网 络就给两个用户发出一个拥塞信号。这些线的交点就是理想的操作点,此时两个用户具有相同的带宽,并且所有的网络带宽都被使用上

如果用户 1 和用户 2 都随着时间推移按加法递增法则增加各自的带宽,我们考虑从某些时候开始带宽的分配会出现什么情况。例如,两个用户以每秒增加 1Mbps 的速度递增各自的发送速率。最终,操作点穿过效率线,两个用户都收到一个从网络传来的拥塞信号在这个阶段,他们必须减少各自分配的带宽。然而,加法递减只会导致他们沿着递增线振荡。这种情况如图 6-24 所示。这种行为将保持操作点接近效率线,但它并不一定是公平的

同样,考虑当两个用户随着时间的推移按乘法(加倍)法则递增带宽直到他们收到个拥塞信号的情况。例如,用户可能每秒增加 10%的发送速率。然后,如果他们乘法递减各自的发送速率,用户的操作点将沿着乘法线振荡。这种行为也如图 6-24 所示。乘法线的斜率和加法线的斜率不同(它指向源点,而加法线呈 45°角)。不过它也没有更好的选择。 在这两种情况下,用户都难以收敛到同时兼顾公平与效率的最佳发送速率。

加法递增乘法递减 (AIMD, Additive Increase Multiplicative Decrease) 法则

用户加法递增他们的带宽,然后当收到拥塞信号时乘法递减他们的带宽。

唯一的其他组合,即乘法递增和加法递减将会偏离最佳点。

AIMD 是 TCP 采用的拥塞控制法则,它基于这个观点和另一个稳定性观点(即驱使网络拥堵非常容易而从中恢复却很难,所以递增政策应轻柔,而递减政策应积极)。但这个法则不是那么的公平,因为 TCP 连接根据每次的往返时间测量值来调整窗口的大小,而不同的连接有不同的往返时间。这导致在所有其他条件都相同的情况下,接近主机的连接比远离主机的连接获得的带宽更多。

TCP 的策略

实际上通常使用的策略不是直接调整发送速率,而是调整滑动窗口的大小。 TCP 就是使用了这种策略。如果窗口大小是 W,往返时间是 RTT,则等价的发送速率是 W/RTT。这种策很容易和流量控制机制结合起来,因为流量控制机制己经使用了一个窗口。同时这种策略还有个优点,发送端可以通过确认来测定数据包的速度,而且如果它停止接收数据包离开网络的报告那么还可以放缓 RTT。

TCP 友好 (TCP-friendly) 拥塞控制

由于 TCP 是 Internet 拥塞控制的主要形式,因此设计新的传输协议要承受相当显著的社会压力,新协议必须能与 TCP 进行公平的竟争。这种现象导致了 TCP 友好 (TCP-friendly) 拥塞控制这一概念的提出。在 TCP 友好的拥塞控制中 TCP 和非 TCP 传输协议可以自由地混合,而没有任何不良影响

6.4 Internet 传输协议:UDP

6.4.1 UDP 概述

UDP 协议的功能

它只是提供了一个与 IP 协议的接口,并在此接口上增加通过端口号复用多个进程的功能,以及可选的端到端错误检功能。这就是 UDP 所做的一切。

UDP 头

UDP preserves message boundaries (保留消息边界) between the sender and the receiver.

UDP 校验和、伪头部

一个可选的校验和 (UDP Checksum) 字段还提供了额外的可靠性。它校验头、数据和一个概念性的 IP 伪头。执行校验和计算时,校验和字段先被设置为零,如果数据字段的长度是奇数则用零填充成偶字节。校验和算法很简单,先按 16 位字的补码相加求和,然后再取总和的补码。因此,当接收端对整个段计算校验和时,要包括 UDP 校验和字段,正确的结果应该为 0。如果发送端没有计算校验和,则将该字段值填为 0,因为补码计算结果可能碰巧真的是 0,则存储为全 1

IPv4 的伪头部如图 6-28 所示,它包含源机器和目标机器的 32 位 IP 地址、UDP 的协议号 (17),以及 UDP 段(包括头)的字节计数。IPv6 的伪头与其类似,但有些许不同。

例:UDP 校验和计算

采用 UDP 而不是原始 IP 的原因

采用 UDP 而不是原始 IP 的最主要价值在于增加了源端口和目标端口。如果没有端口字段,传输层将无从知道如何处理每个入境数据包:而有了端口字段之后,它就能把内嵌的段递交给正确的应用程序处理。

6.5 Internet 传输协议:TCP

6.5.1 TCP 概述

TCP 概况

TCP 字节流

一个 TCP 连接就是一个字节流,而不是消息流。端到端之间不保留消息的边界。例如,如果发送进程将 4 个 512 字节的数据块写到一个 TCP 流中,那么这些数据有可能按 4 个 512 字节块、2 个 1024 字节块、1 个 2048 字节块或者其他的方式被递交给接收进程(见图 6-35)。接收端不管多么努力尝试,都无法获知这些数据被写入字节流时的单元大小。

6.5.2 TCP 服务模型

TCP socket

TCP 服务由发送端和接收端创建一种称为套接字 (socket) 的端点来获得

每个套接字有一个套接字编号(地址),该编号由主机的 IP 地址以及一个本地主机的 16 位数值组成的。这个 16 位数值称为端口 (port),端口是 TCP 的 TSAP 名字。为了获得 TCP 服务,必须显式地在一台机器的套接字和另一台机器的套接字之间建立一个连接

TCP 连接

一个套接字有可能同时被用于多个连接。换句话说,两个或者多个连接可能终止于同个套接字。每个连接可以用两端的套接字标识符来标识,即 (socket, socket2)

所有的 TCP 连接都是全双工的,并且是点到点的。所谓全双工,意味着同时可在两个方向上传输数据:而点到点则意味着待个连接恰好有两个端点。TCP 不支持组播或者广播传输模式

守护进程

这个特殊的守护进程在 UNIX 中称为 inetd(Internet daemon)。当出现一个入境连接请求,inetd 就派生出一个新的进程,在这个进程中调用适当的守护程序,然后由这个守护程序来处理连接请求

知名的 TCP 端口

PUSH 标志位

为了强制将数据发送出去,TCP 有个 PUSH 标志位概念,由数据包携带的 PUSH 标志原意是让应用告诉 TCP 不要延迟传输。

紧急数据

紧急数据 (urgent data) 的处理。当一个具有高优先级数据的应用立即被处理时

当接收方接收到紧急数据时,接收端应用程序被中断(比如,按 UNIX 的术语是得到了一个信号),它停止当前正在做的工作,并且读入数据流以便找到紧急数据。

6.5.3 TCP 协议

TCP 段序号

TCP 连接上的每个字节都有它自己独有的 32 位序号。

数据包携带的 32 位序号可用在一个方向上的滑动窗口以及另一个方向上的确认

TCP 段

TCP 段 (TCP segment) 由一个固定的 20 字节的头(加上可选的部分)以及随后 0 个或者多个数据字节构成。TCP 软件决定了段的大小。它可以将多次写操作中的数据累积起来,放到一个段中发送,也可以将一次写操作中的数据分割到多个段中发送。

有两个因素限制了段的长度。首先,包括 TCP 头在内的每个段,必须适合 IP 的 65515 个字节有效载荷;其次,每个网络都有一个最大传输单元 (MTU, maximum transfer unit)。发送端和接收端的每个段必须适合 MTU,才能以单个不分段的数据包发送和接收。实际上,MTU 通常是 1500 字节(以太网的有效载荷大小),因此它规定了段长度的上界。

TCP 滑动窗口

TCP 实体使用的基本协议是具有动态窗口大小的滑动窗口协议。当发送端传送一段时它启动一个计时器。当该段到达接收方时,接收端的 TCP 实体返回一个携带了确认号和剩余窗口大小的段(如果有数据要发送的话,则包含数据,否则就不包含数据),并且确认号的值等于接收端期望接收的下个序号

TCP 重传

段在传输过程中可能会被延很长时间,因而发送端超时并重传段。重传的段包含的字节范围有可能与原来传输的段的字节范围不同,所以,这就要求仔细管理,以便跟踪哪些字节已经被正确地接收到了。

6.5.4 TCP 段的头

TCP 段头

每个段的起始部分是一个固定格式的 20 字节头。固定的头部之后可能有头的选项。如果该数据段有数据部分的话,那么在选项之后是最多可达 65535-20-20-=65495 个字节的数据,这里的第一个 20 是指 IP 头,第二个 20 指 TCP 头。没有任何数据的 TCP 段也是合法的,通常被用作确认和控制消息。

TCP 连接(五元组)

TCP 端口加上所在主机的 IP 地址组成了 48 位的唯一端点源端点和目标端点一起标识了一条连接。这个连接标识符就称为 5 元组 (5 tuple),因为它由 5 个信息组成:协议 (TCP)、源 IP 地址和源端口号、目标 IP 地址和目标端口号。

序号 (Sequence number) 和确认号 (Acknowledgement number)

序号 (Sequence number) 和确认号 (Acknowledgement number) 字段执行它们的常规功能。请注意,后者指定的是下一个期待的字节,而不是已经正确接收到的最后一个字节它是累计确认 (cumulative acknowledgement)

TCP 头长度 (TCP header length)

TCP 头长度 (TCP header length) 字段指明了 TCP 头包含多少个 32 位的字。

单位是 4 个字节

CWR/ECE

CWR 和 ECE 就用作拥塞控制的信号。当 TCP 接收端收到了来自网络的拥塞指示后,就设置 ECE 以便给 TCP 发送端发 ECN-Echo 信号,告诉发送端放慢发送速率。TCP 发送端设置 CWR,给 TCP 接收端发 CWR 信号,这样接收端就知道发送端已经放慢速率,不必再给发送端发 ECN-Echo 信号。

URG

如果使用了紧急指针 (Urgent pointer),则将 URG 设置为 1。紧急指针指向从当前序号开始找到紧急数据的字节偏移量。这个设施是中断消息的另一种途径。

ACK

ACK 被设置为 1 表示确认号字段是有效的。几乎所有的数据包都会用到这个标志位。 如果 ACK 为 0,则该段不包含确认信息,所以,确认号字段可以被忽略。

PSH

PSH 指出这是被推送 (PUSH) 的数据。特此请求接收端一旦收到数据后立即将数据递交给应用程序,而不是将它缓冲起来直到整个缓冲区满为止

RST

RST 被用于突然重置一个已经变得混乱的连接,混乱有可能是由于主机崩溃,或者其他什么原因造成的。该标志位也可以被用来拒收一个无效的段,或者拒绝一个连接请求

SYN

SYN 被用于建立连接过程。在连接请求中,SYN=1 和 ACK=0 表示该段没有使用捎带确认字段。但是,连接应答捎带了一个确认,因此 SYN=1 和 ACK=1。本质上,SYN 位被用来同时表示 CONNECTION REQUEST 和 CONNECTION ACCEPTED,然后进一步用 ACK 位来区分这两种可能情。

FIN

FIN 被用来释放一个连接。它表示发送端已经没有数据需要传输了。然而,在关闭个连接之后,关闭进程可能会在一段不确定的时间内继续接收数据。

窗口大小 (Window size)

窗口大小 (Window size) 字段指定了从被确认的字节算起可以发送多少个字节。

校验和 (Checksum)

校验和 (Checksum) 提供了额外的可靠性。它校验的范围包括头、数据,以及与 UDP 一样的概念性伪头。除了伪头的协议号为 TCP(6),并且校验和必需强制执行。

选项 (Options)

选项的长度可变长,但必须是 32 位的倍数,不足部分用零填充

最大段长 (MSS, Maximum Segment)

用途最广的选项允许每台主机指定它愿意接受的最大段长 (MSS, Maximum Segment)

在连接建立过程中,每一端可以宣布它的最大段长,并且查看对方给出的最大值。如果一台主机没有使用这个选项,那么它默认可以接受 536 字节的有效载荷。所有 Internet 主机都要求能够接受 536+20=556 个字节的 TCP 段。两个方向上的最大段长可以不同。

窗口尺度 (Window scale)

窗口尺度 (Window scale) 选项允许发送端和接收端在连接建立阶段协商窗口尺度因子。双方使用尺度因子将窗口大小字段向左移动至多 14 位,因此允许窗口最大可达 20 个字节

时间戳 (Timestamp)

时间戳 (Timestamp) 选项携带由发送端发出的时间戳,并被接收端回应。一旦在连接建立阶段启用了它,那么每个数据包都要包含这个选项,主要用来计算来回时间样值,该样值被用在估算多久之后数据包可以被认为丢失。

防止序号回绕 (PAWS, Protection Against Wrapped Sequence numbers) 方案根据时间戳丢弃入境段,从而解决这个序号回绕问题。

选择确认 (SACK, Selective Acknowledgement)

选择确认 (SACK, Selective Acknowledgement) 选项使得接收端可以告诉发送端已经接收到段的序号范围。

UDP TCP 比较

6.5.5 TCP 连接建立

TCP 三次握手

为了建立一个连接,某一端,比如说服务器,必须先依次执行 LISTEN 和 ACCEPT 原语,然后被动地等待入境连接请求并且可以指定只接受一个特定的请求源,也可以不指定

另一端,比如说客户,执行 CONNECT 原语,同时说明它希望连接的 IP 地址和端口它愿意接受的最大 TCP 段长,以及一些可选的用户数据(比如口令)等参数。CONNECT 原语发送一个 SYN 标志位置为 on 和 ACK 标志位置为 off 的 TCP 段,然后等待服务器的响应。

当这个段到达接收方时,那里的 TCP 实体检査是否有一个进程已经在目标端口字段指定的端口上执行了 LISTEN。如果没有,则它发送一个设置了 RST 的应答报文,拒绝客户的连接请求如果某个进程正在该端口上监听,那么,TCP 实体将入境的 TCP 段交给该进程处理该进程可以接受或者拒绝这个连接请求。如果它接受,则发送回一个确认段。

如果两台主机同时企图在一对套接字之间建立连接,则事件序列如图 6-37(b) 所示。这些事件的结果是恰好只建立了一个连接,而不是两个,因为所有的连接都是由它们的端点来标识的。

初始序号

用了一个基于时钟的方案,时钟每 4 微秒滴答一次,由此来解决初始序号问题。

主机崩溃时

6.5.6 TCP 连接释放

TCP 连接释放 1

每个单工连接的释放彼此独立。为了释放一个连接,任何一方都可以发送一个设置了 FN 标志位的 TCP 段,这表示它已经没有数据要发送了。当 FIN 段被另方确认后,这个方向上的连接就被关闭,不再发送任何数据。然而,另一个方向上或许还在继续着无限的数据流。当两个方向都关闭后,连接才算被彻底释放。通常情况下,释放一个连接需要 4 个 TCP 段:每个方向上一个 FIN 和一个 ACK

TCP 连接释放 2

然而,第一个 ACK 和第二个 FIN 有可能被组合在同一个段中,从而将所需段总数降低到 3 个。

TCP 连接释放 3

一个 TCP 连接的两端也可能会同时发送 FIN 段。这两个段按常规的方法被单独确认,然后关闭连接。

释放计时器

为了避免两军对垒问题(在 6.23 节中讨论过的),需要使用计时器。如果在两倍于最大数据包生存期内,针对 FIN 的响应没有出现,那么 FIN 的发送端直接释放连接。

6.5.7 TCP 连接管理模型

连接管理模型

TCP 有限状态机

6.5.8 TCP 滑动窗口

TCP 窗口管理

窗口为 0 时

当窗口变为 0 时,发送端不能如通常那样发送段了,但这里有两种意外情形。第一,紧急数据仍可以发送,比如,允许用户杀掉远程机器上运行的某一个进程。第二,发送端 可以发送一个 1 字节的段,以便强制接收端重新宣告下一个期望的字节和窗口大小。这种数据包称为窗口探测 (window probe)。TCP 标准明确地提供了这个选项,来防止窗口更新数据包丢失后发生死锁

问题

在最差的情况下,当一个字符到达发送端的 TCP 实体,TCP 创建一个 21 字节的 TCP 段,并将它交给 P 组成一个 41 字节的 P 数据报,然后发送出去:在接收端,TCP 立即发送一个 40 字节的确认 (20 字节的 TCP 头加上 20 字节的 IP 头)。以后,当远程终端读取了这个字节之后,TCP 发送一个窗口更新段,它将窗口向前移动 1 个字节。这个数据包也是 40 字节长。最后,当远程终端处理了该字符以后,它发送一个 41 字节的数据包作为该字符的回显。总共累计起来,对于每次敲入的字符,需要使用 162 字节的带宽,并发送 4 个数据段。

延迟确认 (delayed acknowledgement)

延迟确认 (delayed acknowledgement) 的优化方法。基本想法是将确认和窗口更新延退 50 毫秒,希望能够获得一些数据免费搭载过去。

Nagle 算法

Nagle 的建议非常简单:当数据每次以很少量方式进入到发送端时,发送端只是发送第一次到达的数据字节,然后将其余后面到达的字节缓冲起来,直到发送出去的那个数据包被确认;然后将所有缓冲的字节放在一个 TCP 段中发送出去,并且继续开始缓冲字节,直到下一个段被确认。这就是说,任何时候只有第一个发送的数据包是小数据包

Nagle 算法的问题

在 Internet 上玩互动游戏时,玩家通常需要一个快速的短数据包流。如果把更新收集起来以突发的方式发送将使得游戏的响应不稳定

一个更微妙的问题是 Nagle 算法有时可能会延确认的相互作用,从而造成暂时的死锁

禁用 Nagle 算法(这就是所谓的 TCP_NODELAY 选项)

低能窗口综合症 (silly window syndrome)

只有当接收端能够处理它在建立连接时宣告的最大数据段 (MSS),或者它的缓冲区一半为空时(相当于两者之中取较小的值),它才发送窗口更新段。

其他性能问题

接收端的 TCP 也可以缓冲数据,所以它可以阻塞上层应用的 READ 请求,直至它积累了大块的数据。

接收端必须处理的另一个问题是乱序到达的数据段。接收端将缓冲这些数据,直至可以按照顺序递交给应用程序为止。

6.5.9 TCP 计时器管理

重传计时器 (RTO, Retransmission Time Out)

当 TCP 实体发出一个段时,它同时启动个重传计时器。如果在该计时器超时前该段被确认,则计时器被停止。另一方面,如果在确认到来之前计时器超时,则段被重传(并且该计时器被重新启动

重传计时器的问题

动态重传计时器

解决方案是使用一个动态算法,它根据网络性能的连续测量情况,不断地调整超时间隔。

TCP 通常采用的算法是 (Jacobson, 1988) 算法,其工作原理如下所述。对于每一个连接,TCP 维护一个变量 SRTT(Smoothed Round-Trip Time,平滑的往返时间),它代表到达接收方往返时间的当前最佳估计值。当一个段被发送出去时,TCP 启动一个计时器,该计时器有两个作用,一是看该段被确认需要多长时间;二是若确认时间太长,则触发重传动作。如果在计时器超时前确认返回,则 TCP 测量这次确认所花的时间,比如说 R。然后它根据下面的公式更新 SRTT

这里是一个平滑因子,它决定了老的 RTT 值所占的权重。典型情况下\(\alpha=7/8\)。这类公式是指数加权移动平均 (EWMA, Exponentially Weighted Moving Average),或者丢弃样值中噪声的低通滤波器。

在最初的实现中,TCP 使用$2$RTT,但经验表明常数值太不灵活,当发生变化时它不能够很好地做出反应

让超时值对往返时间的变化以及平滑的往返时间要变得敏感。这种改变要求跟踪另一个平滑变量,RTTVAR(往返时间变化, Round-Trip Time Variation),即更新为下列公式

这就像以前的 EWMA,典型地,\(\beta=3/4\)。因此,重传超时值 RTO 为

Karn 算法

当一个段超时并重新发送以后该怎么办?确认到达时,无法判断该确认是针对第一次传输,还是针对后来的重传。

不更新任何重传段的估算值。此外,每次连续重传的超时间隔值加倍,直到段能一次通过为止。这个修正算法称为 Karn 算法 (Karn 和 Partridge, 1987)。

持续计时器 (persiste timer)

第二个计时器是持续计时器 (persiste timer)。它的设计意图是为了避免出现以下所述的死锁情况。接收端发送一个窗口大小为 0 的确认,让发送端等一等。稍后,接收端更新了窗口,但是,携带更新消息的数据包丢失了。现在,发送端和接收端都在等待对方的进一步动作。当持续计时器超时后,发送端给接收端发送一个探询消息。接收端对探询消息的响应是将窗口大小告诉发送端。如果它仍然为 0,则重置持续计时器,并开始下一轮循环。如果它非 0,则现在可以发送数据了。

保活计时器 (keepalive timer)

第三个计时器,即保活计时器 (keepalive timer)。当一个连接空闲了较长一段时间以后,保活计时器可能超时,从而促使某一端査看另一端是否仍然还在。 如果另一端没有响应,则终止连接。

连接终止阶段的计时器

每个 TCP 连接使用的最后一个计时器被应用于连接终止阶段,即在关闭过程中,当连接处于 TIMED WAIT 状态时所使用的计时器。它的超时值为两倍于最大数据包生存期,主要用来确保当连接被关闭后,该连接上创建的所有数据包都已完全消失

6.5.10 TCP 拥塞控制

拥塞窗口

TCP 维持一个拥塞窗口 (congestion window)(cwin),窗口大小是任何时候发送端可以往网络发送的字节数。相应的速率则是窗口大小除以连接的往返时间。TCP 根据 AIMD 规则来调整该窗口的大小

除了维护一个拥塞窗口外,还有一个流量控制窗口,该窗口指出了接收端可以缓冲的字节数。要并发跟踪这两个窗口,可能发送的字节数是两个窗口中较小的那个。

用丢包作为拥塞信号

他观察到丢包可以作为一个合适的拥塞信号

然而,用丢包作为拥塞信号依赖于传输错误相对比较少见的场合。对于诸如 802.11 的无线链路,传输错误比较常见,这也就是为什么它们在链路层有自己的重传机制需要一个良好的重传计时器来准确检测到丢包信号,而且必须以一种及时的方式。我们已经讨论了 TCP 重传计时器如何包括了往返肘间的均值和方差的估算。

ACK clock

首先要考虑的是数据包发送到网络的方式,即使在很短的时间内也必须匹配网络路径,否则就有可能造成拥塞

确认的时间反映了数据包穿越慢速链路后到达接收端的时间。相比在快速链路上的原始数据包,这些确认更分散。随着这些确认穿过网络并传回到发送端,它们保留了这个时序关键的观察是这样的:确认返回到发送端的速率恰好是数据包通过路径上最慢链路时的速率。这正是发送端应该使用的精确发送速率。如果发送端以这个速率往网络注入新的数据包,这些数据包就能以慢速链路允许的速率一样快的速度被转发出去,但它们不会再排队和堵塞沿途上的任何一个路由器。这个时序就是确认时钟 (ack clock)。

慢速启动 (slow start)

第二个考虑是如果网络拥塞窗口从一个很小的规模开始,那么在快速网络上应用 AIMD 规则将需要很长的时间才能达到一个良好的操作点。

当建立连接时,发送端用一个很小的值初始化拥塞窗口,最多不超过 4 个段;然后发送端发送该初始窗口大小的数据。数据包必须经过一个往返时间才被确认对于每个重传计时器超时前得到承认的段,发送端的拥塞窗口增加一个段的字节量。此外,随着该段获得确认,现在网络中又少了一段。结果是每一个被确认的段允许发送两个段,每经过一个往返时间拥塞窗口增加一倍

慢速启动在一定范围内的链接速度和往返时间上都能行之有效,并且使用确认时钟将发送端的传输速率与络路径相匹配。

慢启动阈值 (slow start threshold)/线性增加

为了保持对慢速启动的控制,发送端为每个连接维持一个称为慢启动阈值 (slow start threshold) 的阈值。最初,这个值被设置得任意高,可以达到流量控制窗口的大小,因此它不会限制连接速度。TCP 以慢速启动方式不断增加拥塞窗口,直到发生超时,或者拥塞窗口超过该阈值(或接收端的窗口为满)。

每当检测到丢包,比如超时了,慢启动阈值就被设置为当前拥塞窗口的一半,整个过程再重新启动。

一旦慢速启动超过了阈值,TCP 就从慢速启动切换到线性增加(即加法递增)。

在这种模式下,每个往返时间拥塞窗口只增加一段。像慢速启动一样,这通常也是为每一个被确认的段而不是为每一次 RTT 实施窗口的增加。回忆一下,拥塞窗口 cwnd 和最大段长 MSS,一个常见的近似做法是这样的:针对 cwnd/MSS 中可能被确认的每个数据包,将 cwnd 增加 (MSS\(\times\)MSS)/cwnd。

快速重传 (fast retransmission)

发送端有一个快速方法来识别它的包已经被丢失。当丢失数据包的后续数据包到达接收端时,它们触发给发送端返回确认。这些确认段携带着相同的确认号,称为重复确认 (duplicate acknowledgement)。发送端每次收到重复确认时,很可能另一个包已经到达接收端,而丢失的那个包仍然没有出现。

TCP 有点随意地假设三个重复确认意味着已经丢失一个包。丢失包的序号可以从确认号推断出来,它是整个数据序列中紧接着的下一个数据包。因此,这个包可以被立即重传,在其计时器超时前就重新发送出去

这种启发式机制称为快速重传 (fast retransmission)。重传后,慢启动阈值被设置为当前拥塞窗口的一半,就像发生了超时一样。重新开始慢启动过程,拥塞窗口被设置成一个包。有了这个窗口大小,如果在一个往返时间内确认了该重传的数据包以及丢包之前已发送的所有数据,则发出一个新的数据包。

快速恢复 (Fast recovery)

快速恢复 (Fast recovery) 就是实现这种行为的启发式机制。这是一个临时模式,其目的是保持拥塞窗口上运行确认时钟,该拥塞窗口有一个新阈值或者快速重传时把拥塞窗口值减半。要做到这一点,对重复确认要计数(包括触发快速重传机制的那三个重复确认)直到网络内的数据包数量下降到新阈值。这大概需要半个往返时间。从此时往后,每接收到一个重复确认就发送一个新的数据包。快速重传之后的一个往返时间后,丢失的包将被确认。在这个时间点,重复确认流将停止,快速恢复模式就此退出。拥塞窗口将被设置到新的慢启动阈值,并开始按线性增长

这种启发式的结果是 TCP 避免了慢速启动,只有第一次启动或者发生超时时才进入真正的慢速启动。当发生多个数据包丢失时,快速重传机制不足以恢复,仍然可能会发生超时。此时,不是反复地进入慢速启动,而是当前连接的拥塞窗口遵循一种锯齿 (sawtooth) 模式,即加法递增(每个 RTT 增加一段)和乘法递减(每个 RTT 减半)。这正是我们力求实现的 AIMD 规则。

选择确认 (SACK, Selective Acknowledgement)

该确认列出了 3 个已接收的字节范围。有了这个信息,发送端在实现拥塞窗口时可以更直接地确定哪些数据包需要重传,并跟踪那些还在途中的数据包。

当发送端和接收端建立连接时,它们各自在 TCP 段的选项字段发送允许 SACK,通知对方它们理解选择确认。

显式拥塞通知 (ECN, Explicit Congestion Notification)

当发送端和接收端在建立阶段过程中设置了 ECE 和 CWR 标志位,双方均表示它们能够使用这些标志位后,该 TCP 连接就可启用 ECN。如果使用 ECN,每个携带 TCP 段的数据包在 IP 头打上标记,表明它可以携带 ECN 信号。支持 ECN 的路由器在接近拥塞时就会在携带 ECN 标志的数据包上设置拥塞信号,而不是在拥塞发生后丢弃这些数据包。如果到达的任何数据包携带了 ECN 拥塞信号,则会告知 TCP 接收端。然后接收端使用 ECE(ECN Echo) 标志位给 TCP 发送端发通知,告知它的数据包经历了拥塞。发送端通过拥塞窗口少 (CWR, Congestion Window Reduced) 标志位告诉接收端它已经收到拥塞信号了。

TCP 发送端收到这些拥塞通知消息的反应和它根据重复确认检测到丢包的处理方式完全相同。

TCP 拥塞控制总结

作者

xqmmcqs

发布于

2021-07-21

更新于

2023-03-29

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×