TCP协议详解,《计算机网络自顶向下》笔记

第17章、TCP:传输控制协议

1、TCP服务

  • TCP提供一种面向连接的,可靠的字节流服务
  • TCP通过下列方式提供可靠性
    • 应用数据分段
    • 定时器重传
    • 接收端发送确认
    • 保持首部和数据的检验和
    • TCP对收到的数据进行重新排序
    • 流量控制

2、TCP首部

第18章 TCP连接的建立和终止

18.1 引言

tcp是一个面向连接的协议,发送数据前必须先建立一条连接。

18.2 连接的建立和终止

18.2.3 建立连接协议

18.2.5 正常tcpdump输出

18.3 连接建立的超时

大多数伯克利系统将建立一个新连接的最长时间限制为75秒

18.3.1 第一次超时时间

第一次超时时间为5.8秒,接近6秒,但是不准确,相比之下第二个超时时间几乎准确地为24秒,为什么?
因为第一次调用定时器地时候,系统内核可能不会立即响应,它可能会先处理其它中断。但是超时之后,系统内核立即调用到了该定时器,之后立即修改,因此以后的超时时间会更准确。 — 这个解释有些瑕疵。

18.4 最大报文长度

  • 最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。
  • 当一个连接建立时,连接的双方都要通告各自的MSS。(MSS选项只能出现在SYN报文段中)
  • 缺陷: 如果两端的主机都连接到以太网上,都采用536的MSS,但中间网络采用296的MTU,也将会出现分段。使用路径上的MTU发现机制(参见24.2节)是关于这个问题的唯一方法。

  • 图例

18.5 TCP的半关闭

  • TCP提供了连接的一端在结束它的发送之后,还能接收来自另一端数据的能力,这就是所谓的半关闭。
  • 如果应用程序不调用close而调用shutdown,且第2个参数值为1,则插口的API支持 半关闭。
  • 图例:

  • 为什么要有半关闭?以rsh命令为例,没有半关闭,需要其他的一些技术让客户通知服务器, 客户端已经完成了它的数据传送,但仍要接收来自服务器的数据。使用两个TCP连接也可作为一个选择,但使用半关闭的单连接更好。

18.6 TCP状态变迁图

18.6.1 2MSL等待时间

  • TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前,在网络内的最长时间。
  • RFC793指出MSL为2分钟。然而,实现中的常值是30秒,1分钟,2分钟。
  • 处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)
  • 2ML时间内的本地端口不能再被使用。
  • SO_REUSEADDR选项,让调用者对处于2MSL等待的本地端口进行赋值。

18.6.2 平静时间的概念

为了防止这种情况,RFC 793指出TCP在重启动后的MSL秒内不能建立任何连接。这就称 为平静时间(quiet time)。

18.6.3 FIN_WAIT_2状态

  • FIN_WAIT_2状态可能被永远保持
  • 许多伯克利实现采用如下方式来防止这种在FIN_WAIT_2状态的无限等待。如果执行主动关闭的应用层将进行全关闭,而不是半关闭来说明它还想接收数据,就设置一 个定时器。如果这个连接空闲10分钟75秒,TCP将进入CLOSED状态。

18.7 复位报文段

一般来说,无论何时一个报文段发往基准的连接出现错误,TCP都会发出一个复位报文段。

18.7.1 到不存在的端口的连接请求

产生复位的一种常见的情况是,当连接请求到达时,目的端口没有进程正在听。

  • 图例

  • 说明:需要注意的值是复位报文段中的序号字段和确认序号字段。 序号字段为0:因为1中的报文,ACK比特没有被置为1,因此复位报文段中的序号被置为0. 确认序号为297416194:确认序号的值 = 进入的ISN + 数据字节数,因为SYN比特从逻辑上占用1个字节的序号空间,因此,确认序号为297416193(ISN) + 0(数据长度)+ 1(SYN比特

18.7.2 异常终止一个连接

  • 在18.2节中看到终止一个连接的正常方式是一方发送FIN。有时这也称为有序释放。
  • 但也有可能发送一个复位报文段而不是FIN来中途释放一个连接。有时称这为异常释放。
  • 异常终止一个连接对应用程序来说有两个优点:(1)丢弃任何待发数据并立即发送复位 报文段;(2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭。
  • Socket API通过“linger on close”选项 (SO_LINGER)提供了这种异常关闭的能力。
  • 图例:

  • 需要注意的是RST报文段不会导致另一端产生任何响 应,另一端根本不进行确认。收到RST的一方将终止该连接,并通知应用层连接复位。

18.7.3 检测半打开连接

如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半 打开(Half-open)的。 任何一端的主机异常都可能导致发生这种情况。只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。

  • 半打开连接的另一个常见原因是当客户主机突然掉电而不是正常的结束客户应用程序后再关机。
  • TCP的处理原则是接收方以复位作为应答。
  • 图例

18.8 同时打开

  • 要每一方使用一个对方熟知的端口作为本地端口。
  • 例如,主机A中的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。 主机B中的应用程序则使用本地端口8888,并与主机A的端口7777执行主动打开。
  • 图例

  • 一个同时打开的连接需要交换 4个报文段,比正常的三次握手多一个。此外,要注意的是 我们没有将任何一端称为客户或服务器,因为每一端既是客户又是服务器。

18.9 同时关闭

  • 我们在以前讨论过一方(通常但不总是客户方)发送第一个FIN执行主动关闭。双方都执行主动关闭也是可能的,TCP协议也允许这样的同时关闭( simultaneous close)。
  • 图例

18.10 TCP选项

  • 选项类型

  • 每个选项的开始是1字节kind字段,说明选项的类型。kind字段为0和1的选项仅占1个字节。 其他的选项在kind字节后还有len字节。它说明的长度是指总长度,包括kind字节和len字节。
  • 设置无操作选项的原因在于允许发方填充字段为 4字节的倍数。

18.11 TCP服务器的设计

需要回答下面的问题: 当一个服务器进程接受一来自客户进程的服务请求时是如何处理端口的? 如果多个连接请求几乎同时到达会发生什么情况?

18.11.1 TCP服务器端口号

  • 再次重申TCP使用由本地地址和远端地址组成的4元组:目的IP地址、目的端口号、源IP地址和源端口号来处理传入的多个连接请求。

18.11.2 限定的本地IP地址

  • 多接口服务器下,当服务器不能任选其本地IP地址而必须使用特定的IP地址时的情况。

18.11.3 限定的远端IP地址

  • 在11.12节,我们知道UDP服务器通常在指定IP本地地址和本地端口外,还能指定远端IP地址和远端端口。
  • 三种类型的地址绑定规则

最常使用的绑定(第 1行,如果支持的话)将最先尝试, 最不常用的(最后一行两端的 I P地址都没有制定)将最后尝试。

18.11.4 呼入连接请求队列

  • 处于被动连接请求的服务器应该始终准备处理下一个呼入的连接请求。那正是使用并发服务器的根本原因。
  • 问题:当服务器正处于忙时,TCP是如何处理这些呼入的连接请求?
  • 伯克利的TCP采用以下规则:

第19章 TCP的交互数据

19.1 引言

  • TCP需要同时处理成块数据和交互数据,但是使用的处理算法有所不同。
    • 成块数据和交互数据是如何区分的?
  • 经受时延的确认以及Nagale算法

19.2 交互式输入

  • Rlogin每次总是发送一个字节到服务器。
  • Rlogin输入的例子

19.3 经受时延的确认

  • 通常TCP在接收到数据时并不立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时 称这种现象为数据捎带ACK)。绝大多数实现采用的时延为 200 ms,也就是说,TCP将以最大 200 ms的时延等待是否有数据一起发送。Linux上默认是40ms。
  • Host Requirements RFC声明TCP需要实现一个经受时延的ACK,但时延必须小于 500 ms。

19.4 Nagle算法

  • 该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的分组,并在确认到来时以一个分组的方式发出去。
  • 优点:它是自适应的:确认到达得越快,数据也就发 送得越快。而在希望减少微小分组数目的低速广域网上,则会发送更少的分组。
  • 例子: 通过使用Nagle 算法,为发送16个字节的数据客户只需要使用 9个报文段,而不再是16个。

Nagle算法的实现规则:

  1. 如果包长度达到MSS,则允许发送;
  2. 如果该包含有FIN,则允许发送;
  3. 设置了TCP_NODELAY选项,则允许发送;
  4. 未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;
  5. 上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

19.4.1 关闭Nagle算法

关闭Nagle算法的场景:一个典型的例子是 X窗口系统服务器(见 30 . 5节):小消 息(鼠标移动)必须无时延地发送,以便为进行某种操作的交互用户提供实时的反馈。

  • 插口API用户可以使用TCP_NODELAY选项来关闭Nagle算法。
  • Host Requirements RFC声明TCP必须实现Nagle算法,但必须为应用提供一种方法来关闭该算法在某个连接上执行。

19.4.2 一个例子

19.5 窗口大小通告

19.6 小结

交互数据总是以小于最大报文段长度的分组发送。在Rlogin中通常只有一个字节从客户发 送到服务器。Telnet允许一次发送一行输入数据,但是目前大多数实现仍然发送一个字节。 对于这些小的报文段,接收方使用经受时延的确认方法来判断确认是否可被推迟发送, 以便与回送数据一起发送。这样通常会减少报文段的数目,尤其是对于需要回显用户输入字符的Rlogin会话。 在较慢的广域网环境中,通常使用Nagle算法来减少这些小报文段的数目。这个算法限制发送者任何时候只能有一个发送的小报文段未被确认。但我们给出的一个例子也表明有时需要禁止Nagle算法的功能。

第20章 TCP的成块数据

20.1 引言

本章我们将 1、介绍TCP所使用的被称为滑动窗口协议的另一种形式的流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于 发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。 2、介绍TCP的PUSH标志。 3、介绍慢启动,TCP使用该技术在一个连接上建立数据流。 4、介绍成块数据流的吞吐量。

20.2 正常数据流

我们以从主机svr4单向传输8192个字节到主机bsdi开始,客户向网络发送 8个1024字节的数据。

  • 发送方首先传送3个数据报文段(4 ~ 6)。下一个报文段(7)仅确认了前两个数据报文段, 这可以从其确认序号为2049而不是3073看出来。

    报文段7的ACK的序号之所以是2048而不是3073是由以下原因造成的: 当一个分组到达,会将其放置到IP的输入队列中。 IP将按同样顺序将它们交给TCP。TCP处理报文4时,该连接被标记为产生一个经受延时的确认。TCP处理下一个报文段(5),由于TCP现在有两个未完成的报文段需要确认,因此产生一个序号为2048的ACK(报文段7),并清楚该连接产生经受延时的确认标志。TCP处理下一个报文段( 6),而连接又被标志为产生一个经受时延的确 认。在报文段9到来之前,由于时延定时器溢出,因此产生一个序号为3073的ACK(报文段8)。

  • 报文段11 ~ 1 6说明了通常使用的“隔一个报文段确认”的策略。即两个发送的报文会回复一个ACK。图20-1和图20-2都说明了这个现象。

快的发送方和慢的接收方

图20-3显示了另外一个时间系列。这次是从一个快的发送方(一个Sparc工作站)到一个慢的接收方(配有慢速以太网卡的80386机器)。它的动态活动情况又有所不同。

  • 发送方发送4个背靠背报文,的数据报文段去填充接收方的窗口,然后停下来等待一个ACK(因为窗口只有4096字节,所以最多只能发送4个1024字节报文)。
  • 接收方发送ACK(报文段8),但通告其窗口大小为 0,这说明接收方已收到所有数据,但这些数据都在接收方的TCP缓冲区,因为应用程序还没有机会读取这些数据。

  • 另一个ACK(报文段9)(称为窗口更新)在17.4 ms后发送,表明接收方现在可以接收另外的4096个字节的数据。虽然这看起来像一个ACK,但由于它并不确认任何新数据,只是用来增加窗口的右边沿,因此被称为窗口更新。

  • 发送方发送最后4个报文段(10 ~ 13),再次填充了接收方的窗口。注意到报文段 13中包括 两个比特标志:PUSH和FIN。

  • 随后从接收方传来另外两个ACK,它们确认了最后的4096字节 的数据(从4097到8192字节)和FIN(标号为8192)。

总结:快的发送方会迅速填满窗口,等待ACK报文,第一个ACK报文说明报文已经接收到,但是应用层未读取;第二个ACK报文更新窗口,使发送方可以继续发送报文。

20.3 滑动窗口

  • 窗口合拢:窗口左边沿向右边沿靠近。

  • 窗口张开:当窗口右边沿向右移动时将允许发送更多的数据。

  • 窗口收缩:当右边沿向左移动时。Host Requirements RFC强烈建议不要使 用这种方式。

一个例子

1) 发送方不必发送一个全窗口大小的数据。 2) 来自接收方的一个报文段确认数据并把窗口向右边滑动。这是因为窗口的大小是相对于确认序号的。 3) 正如从报文段7到报文段8中变化的那样,窗口的大小可以减小,但是窗口的右边沿却不能够向左移动。 4) 接收方在发送一个ACK前不必等待窗口被填满。在前面我们看到许多实现每收到两个报文段就会发送一个ACK。

20.4 窗口大小

由接收方提供的窗口的大小通常可以由接收进程控制,这将影响TCP的性能。 4.2BSD默认设置发送和接受缓冲区的大小为2048个字节。在4.3BSD中双方被增加 为4096个字节。其他的系统,如Solaris 2.2、4.4BSD和AIX3.2则使用更大的默认缓存大小,如8192或16384等

在20.7节中,我们将看到在给定通信媒体带宽和两端往返时间的情况下,如何计算最小的缓存大小。

一个例子,窗口扩大为6144的情况

首先注意到的是在报文段2中提供的窗口大小为6144字节。 image.png

  • 由于这是一个较大的窗口,因此客户立即连续发送了 6个报文段(4 ~ 9),然后停止。
  • 报文段10确认了所有的数据(从第 1到 6144字节),但提供的窗口大小却为2048。

  • 报文段11和12完成了客户的数据传输,且最后一个报文段带有FIN标志。

  • 报文段13包含与报文段10相同的确认序号,但通告了一个更大的窗口大小。

  • 报文段14确 认了最后的2048字节的数据和FIN。

  • 报文段15和16仅用于通告一个更大的窗口大小。 许多TCP实现,在窗口大小增加了两个最大报文段长度(本例中为2048字节,因为MSS为1024字节)或者最大可能窗口的50%(本例中为2048字节,因为最大窗口大小为4096字节)时发送这个窗口更新

  • 报文段17和18完成通常的关闭过程。

20.5 PUSH标志

  • 发送方使用该标志通知接收方将所收到的数据全部提交给接收进程。这里的数据包括与PUSH一起传送的数据以及接收方TCP已经为接收进程收到的其他数据。

  • 目前大多数的API没有向应用程序提供通知其TCP设置PUSH标志的方法。的确, 许多实现程序认为PUSH标志已经过时,一个好的TCP实现能够自行决定何时设置这个标志

举例,说明什么时候TCP会自动设置PUSH标志

图20-1和图20-7的不同之处在于:图20-1发送8次,每次发送1024字节;而图20-7只发送1次,发送8192字节

  • 图20-1,因为客户进行了8次1024字节数据的写操作,并且每次写操作均清空了发送缓存。

  • 图20-7,虽然指定写的是8192个字节的数据,但发送方的发送缓存却是4096个字节。

  • 图20-3,之所以看到前4个报文段(4 ~ 7)的标志被设置,是因为它们每一个均使 TCP产生了一个报文段并提交给IP层。

20.6 慢启动

为什么要有慢启动

在本章所有的例子中,发送方一开始便向网络发送多个报文段,直至达到接收方通告的窗口大小为止。 但是如果在发送方和接收方之间存在多个路由器和速率较慢的链路时,就有可能出现一些问题:一些中间路由器必须缓存分组,并有可能耗尽存储器的空间。

什么是慢启动

慢启动是一种算法,该算法通过观察到新分组进入网络的速率应该与另一端返回确认的速率相同而进行工作。

慢启动为发送方的TCP增加了另一个窗口:拥塞窗口 (congestion window),记为cwnd。

每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。

一个例子

图20 - 8表示的是将从主机s u n发送到主机vangogh.cs.berkeley.edu的数据。

20.7 成块数据的吞吐量

让我们看一看窗口大小、窗口流量控制以及慢启动对传输成块数据的TCP连接的吞吐量的相互作用。 随着时间的推移,发送方和接收方之间的管道 (pipe)被填满。此时不论拥塞窗口和通告窗口是多少,它都不能再容纳更多的数据。

20.7.1 带宽时延乘积

现在来回答窗口应该设置为多大的问题。窗口大小 = 带宽时延乘积

可以计算通道的容量为:

 c a p a c i t y (bit) = b a n d w i d t h (b/s) × ro u n d-trip time (s)

一般称之为带宽时延乘积。这个值依赖于网络速度和两端的 RTT,可以有很大的变动。

20.7.2 拥塞

当数据到达一个大的管道(如一个快速局域网)并向一个较小的管道(如一个较慢的广域网)发送时便会发生拥塞。

  • 在图20 - 13中已经假定发送方不使用慢启动,它按照局域网的带宽尽可能快地发送编号 为1 ~ 20的报文段(假定接收方的通告窗口至少为 20个报文段)。正如我们看到的那样, ACK之间的间隔与在最慢链路上的一致。假定瓶颈路由器具有足够的容纳这20个分组的缓存。 如果这个不能保证,就会引起路由器丢弃分组。在 21.6节讨论避免拥塞时会看到怎样避免这种情况。

20.8 紧急方式

什么是紧急方式

TCP提供了“紧急方式 ( urgent mode)”,它使一端可以告诉另一端有些具有某种方式的 “紧急数据”已经放置在普通的数据流中。另一端被通知这个紧急数据已被放置在普通数据流 中,由接收方决定如何处理。

怎么使用紧急方式

设置TCP首部中的两个字段来发出这种从一端到另一端的紧急数据已经被放置在数据流中的通知。 URG比特被置1,并且一个16bit的紧急指针被置为一个正的偏移量,该偏移量必须与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。

为什么使用紧急方式

Telnet和Rlogin从服务器到客户使用紧急方式,在窗口关闭的情况下,也可以发送数据。

  • 客户的TCP停止(也即,客户通告了一个大小为0的窗口)。
  • 服务器进程进入了紧急方式,尽管它不能够发送任何数据,服务器TCP也会立即发送紧急指针和URG标志。
  • 当客户TCP接收到 这个通知时就会通知客户进程。
  • 于是客户可以从服务器读取其输入、打开窗口并使数据流动。

一个例子,接收方窗口关闭的情况下,TCP是如何发送紧急数据的

服务端:在主机bsdi上启动sock程序,并使之在连接建立后和从网络读取前暂停 10秒种(通过使用 - P选 项),这将使另一端填满发送窗口。 客户端:在主机sun上启动客户,使之使用一个8192字节的发送缓存(使用 -S选项) 并进行6个向网络写1024字节数据的操作(使用 - n选项)。还指明 -U5选项,告知它向网络写第5个缓存之前要写 1个字节的数据,并进入紧急数据方式。

  • 紧急数据可以在对端窗口通告为0的情况下发送出去。
  • 每一次调用输出系统调用时,都会发送紧急数据。

20.9 小结

进行成块数据有效传输的最重要的方法是TCP的滑动窗口协议。我们考察了TCP为使发送方和接收方之间的管道充满来获得最可能快的传输速度而采用的方法。我们用带宽时延乘积衡量管道的容量,并分析了该乘积与窗口大小之间的关系。在24.8节介绍TCP性能的时候将再次涉及这个概念。

我们还介绍了TCP的PUSH标志,因为在跟踪结果中总是观察到它,但我们无法对它的设置与否进行控制。 本章最后一个主题是TCP的紧急数据,人们常常错误地称其为“带外数据”。 TCP的紧急方式只是一个从发送方到接收方的通知,该通知告诉接收方紧急数据已被发送, 并提供该数据最后一个字节的序号。应用程序使用的有关紧急数据部分的编程接口常常都不是最佳的,从而导致更多的混乱。

21 TCP的超时和重传

21.1 引言

  • TCP提供可靠的传输层,方法之一就是确认从另一端收到的数据。
  • 当数据和确认可能丢失时,TCP通过超时和重传的策略解决。
  • 对每一个连接,TCP管理4个不同的定时器
    • 重传定时器
    • 坚持(persist)定时器
    • 保活(keepalive)定时器
    • 2MSL定时器

21.2 超时和重传的简单例子

在Linux系统中,重传时间为1,2,4,8,16,32的时间间隔。这个倍数关系称为“指数退避”。 /proc/sys/net/ipv4/tcp_synack_retries 表示SYN+ACK的重传次数。 /proc/sys/net/ipv4/tcp_syn_retries 表示SYN的重传次数。

21.3 往返时间测量

TCP超时与重传中最重要的部分就是对一个给定连接的往返时间(RTT)的测量

RTT可能经常会发生变化,TCP应该跟踪这些 变化并相应地改变其超时时间。

RTT算法:被平滑的RTT估计器,跟踪RTT的方差,Karn算法。

21.4 往返时间RTT的例子

检查TCP的超时和重传、慢启动以及拥塞避免等方方面面的实现细节。 往返时间RTT是有TCP内部的算法进行自适应的计算,以估计实际的网络情况,避免网络拥塞时的大量重传报文。该章节阐述了TCP的内部的算法,学术研究有用。

21.4.1 往返时间RTT的测量

21.4.2 RTT估计器的计算

21.4.3 慢启动

21.5 拥塞举例

发送方主机slip总是通告窗口大小为4096,而接收方主机vangogh则通告窗口为 8192。

发送方

  • 报文段45丢失或损坏了。观察其后的ACK报文,紧接着的是带有相同序号的8个ACK。正是接收到报文段62,也就是第3个重复ACK,才引起自序号6657开始的数据报文段(报文段63)进行重传 源于伯克利的TCP实现对收到的重复ACK进行计数,当收到第3个时,就假定一个报文段已经丢失并重传自那个序号起的一个报文段。这就是Jacobson的快速重传算法。

  • 注意到在重传后(报文段63),发送方继续正常的数据传输(报文段67、69和71)。TCP不需要等待对方确认重传。

接收方

  • 当按序收到正常数据(报文段43)后,接收TCP将255个字节的数据交给用户进程。
  • 报文段46是失序的:数据的开始序号(6913)并不是下一个期望的序号(6657)。 TCP保存256字节的数据,并返回一个已成功接收数据的最大序号加 1(6657)的ACK。
  • 被vangogh接收到的后面 7个报文段(48,50,52,54,55,57和59)也是失序的,接收方TCP保存这些数据并产生重复ACK。 目前TCP尚无办法告诉对方缺少一个报文段,也无法确认失序数据。此时主机vangogh所能够做的就是继续发送确认序号为6657的ACK。如果失序报文将窗口填满了怎么办,应用程序又无法读取失序报文,发送方也因为窗口0而无法发送重发的报文
  • 当缺少的报文段(报文段63)到达时,接收方TCP在其缓存中保存第6657 ~ 8960字节的数据,并将这2304字节的数据交给用户进程。
  • 所有这些数据在报文段72中进行确认。
  • 请注意此 时该ACK通告窗口大小为5888(8192-2304),这是因为用户进程还没有读取这些已准备好 的2304字节的数据。

接收方接收到失序的报文段会保存该数据

21.6 拥塞避免算法

慢启动和拥塞避免算法

  • 慢启动算法是在一个连接上发起数据流的方法,但有时我们会达到中间路由器的极限,此时分组将被丢弃。拥塞避免算法是一种处理丢失分组的方法。

  • 拥塞避免算法和慢启动算法是两个目的不同、独立的算法。它们都是用来操作拥塞窗口cwnd的。

  • 拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。

    拥塞避免和慢启动工作流程

拥塞避免算法和慢启动算法需要对每个连接维持两个变量:一个拥塞窗口cwnd和一个慢启动门限ssthresh。

  • 慢启动算法:初始设置cwnd为1个报文段,此后每收到一个确认就加 1。
  • 拥塞避免算法:要求每次收到一个确认时将 cwnd增加1 / cwnd。

1) 对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。 2) TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。 3) 当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半( cwnd和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(这就是慢启动)。 (只看到了ssthresh减少的算法,sshtresh什么时候增加呢,cwnd窗口越来越大时,拥塞发生之后,ssthresh虽然变为了cwnd的一半,但是还是增大了) 4) 当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。 慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤 2 中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。

21.7 快速重传与快速恢复算法

  • 快速重传算法 当发送方发送报文之后,接收到了3个或3个以上的重复ACK,则重传丢失的数据报文段,而无需等待该报文的超时定时器溢出。
  • 快速恢复算法 快速重传之后,接下来执行的不是慢启动算法,而是拥塞避免算法。 1)当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。 2)每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送 1个分组(如果新的cwnd允许发送)。 3)当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤 1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第 1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥塞避免,因为当分组丢失时我们将当前的速率减半。

21.8 拥塞举例(续)

  • 使用tcpdump来截获所有的发送和接收的报文段, 并通过使用 -D选项来打开插口排错功能,就会在发送每一个报文段时看到cwnd和ssthresh的值。

  • 如果MSS为256字节,则cwnd和ssthresh的初始 值分别为256和65535字节。
  • 这一节主要分析快速恢复算法。(这个版本的快速恢复算法有点问题,在快速恢复算法中,当cwnd大于ssthresh时,使用的还好是慢启动

21.8.1 慢启动到拥塞避免举例

  • 慢启动快速增大cwnd,cwnd = cwnd + 1,到cwnd大于ssthresh。之后启用拥塞避免算法,cwnd <-- cwnd + 1 / cwnd

21.8.2 快速恢复算法举例

  • 当最初的2个重复的ACK(报文段6 0和6 1)到达时它们被计数,而cwnd保持不变(也就是图21 - 10中处理重传之前的平坦的一段)。
  • 当第3个重复的ACK到达时,ssthresh = cwnd / 2cwnd = ssthresh + 3 * MSS
  • 又有5个重复的ACK到达(报文段64~66, 68和70),每次cwnd增加1个报文段长度。
  • 最后一个新的ACK(报文段72段)到达时,cwnd被置为ssthresh(1024)并进入正常的拥塞避免过程。由于新的ACK到达,而且由于cwnd小于等于ssthresh(现在相等),因此报文段的大小增加到cwnd,取值为1280。
  • cwnd的取值小于未被确认的数据的时候,不能发送任何数据。
Share: X (Twitter) Facebook LinkedIn