《计算机网络——自顶向下方法》学习笔记——运输层

运输层

运输层位于应用层和网络层之间,是分层的网络体系结构的重要部分。
该层为运行在不同主机上的应用进程提供直接的通信服务起着至关重要的作用。

概述和运输层服务

运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信(logic communication)功能。

运输层协议是在端系统中而不是在路由器中实现的。

在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组,用因特网术语来讲该分组称为运输层报文段(segment)。

在这里插入图片描述

运输层和网络层的关系

运输层和网络层之间的关系,类比 两个家庭间相互邮件通信:

在这里插入图片描述
运输层协议只工作在端系统中。
在端系统中,运输层协议将来自应用进程的报文移动到网络边缘(即网络层),反过来也是一样,但对有关这些报文在网络核心如何移动并不作任何规定。

计算机网络中可以安排多种运输层协议,每种协议为应用程序提供不同的服务模型。

运输协议能够提供的服务常常受制于底层网络层协议的服务模型。
如果网络层协议无法为主机之间发送的运输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证。

然而,即使底层网络协议不能在网络层提供相应的服务,运输层协议也能提供某些服务。

因特网运输层概述

因特网为应用层提供了两种截然不同的可用运输层协议:UDP(用户数据报文)、TCP(传输控制协议)。

UDP: 为调用它的应用程序提供了一种不可靠、无连接的服务。
TCP: 为调用它的应用程序提供了一种可靠的、面向连接的服务。

将运输层分组称为报文段(segment)。

因特网文献(如RFC文档)也将TCP的运输层分组称为报文段,而常将UDP的分组称为数据报(datagram)。
这类因特网文献也将网络层分组称为数据报

因特网网络层协议有一个名字叫IP,即网际协议。IP为主机之间提供了逻辑通信。

IP的服务模型是尽力而为交付服务(best-effort delivery service)。

IP被称为不可靠服务(unreliable service):它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。

每台主机至少有一个网络层地址,即所谓的IP地址。

UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。
将主机间交付扩展到进程间交付被称为运输层的多路复用(transport-layer multiplexing)与多路分解(demultiolexing)。

TCP为应用程序提供了几种附加服务:提供可靠数据传输(reliable data transfer)、拥塞控制(congestion control)。

UDP流量是不可调节的。

多路复用与多路分解

运输层的多路复用与多路分解,也就是将由网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务。

一个进程(作为网络应用的一部分)有一个或多个套接字(socket),它相当于从网络向进程传递数据和从进程向网络传递数据的门户。

在这里插入图片描述如图所示,

在接收主机中的运输层实际上并没有直接将数据交付给进程,而是将数据交给了一个中间的套接字。
由于在任一时刻,在接收主机上可能有不止一个套接字,所以每个套接字都有唯一的标识符。
标识符的格式取决于它是UDP还是TCP套接字。

将运输层报文段中的数据交付到正确的套接字的工作称为多路分解(demultiplexing)。

在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层,所有的这些工作称为多路复用(multiplexing)。

运输层多路复用要求:
1)套接字有唯一标识符;
2)每个报文段有特殊字段来指示该报文段所要交付到的套接字。

在这里插入图片描述如图所示,这些特殊字段是源端口号字段(source port number field)和目的端口号字段(destination port number field)。

0~1023范围的端口号称为周知端口号(well-known port number),是受限制的,这是指它们保留给诸如HTTP(它使用端口号80)和FTP(它使用端口号21)之类的周知应用层协议来使用。

1. 无连接的多路复用与多路分解

clientSocket = socket(AF_INET, SOCK_DGRAM)
以上述方式创建一个UDP套接字时,
运输层自动为该套接字分配一个端口号。
运输层从范围1024~65536内分配一个端口号,
该端口号是当前未被该主机中任何其他UDP端口使用的。
另一种方法,通过bind为这个UDP套接字关联一个特定的端口号。
clientSocket.bind((’’, 19157))
如果应用程序开发者所编写的代码实现的是一个"周知协议"的服务器端,那么开发者需为其分配一个相应的周知端口号。

通常,应用程序的客户端让运输层自动地分配端口号,服务器端则分配一个特定的端口号。

假定:

主机A的一个进程有UDP端口19157,
它要发送一个应用程序数据块,给位于主机B中的另一个进程,
该进程有UDP端口46428。
主机A中的运输层创建一个运输层报文段,
其中包括应用程序数据,源端口号,目的端口号和两个其他值。
运输层将得到的报文段传递到网络层。
网络层将该报文段封装到一个Ip数据报中,尽力而为地将报文段交付给接收主机。
如该报文段到达接收主机B,
接收主机运输层就检查该报文段中的目的端口号,
并将该报文段交付给端口号所标识的套接字

主机B能运行多个进程,
每个进程有自己的UDP套接字及相应的端口号。

主机B可能运行多个进程,每个进程有其自己的UDP套接字,及相联系的端口号。
当UDP报文段从网络到达时,主机B通过检查该报文段中的目的端口号,
将每个报文段定向到相应的套接字。

一个UDP套接字由一个二元组全面标识,该二元组包含一个目的IP地址和一个目的端口号。
因此,如两个UDP报文段有不同的源Ip地址或源端口号,但有相同的目的IP地址目的端口号,则这两个报文段将通过相同的目的套接字被定向到相同的目的进程。

在这里插入图片描述

2. 面向连接的多路复用与多路分解

TCP套接字和UDP套接字之间的一个细微差别是,TCP套接字是由一个四元组(源IP地址,源端口号,目的IP地址,目的端口号)来标识的。

当一个TCP报文段从网络到达一台主机时,该主机使用全部4个值来将报文段定向到相应的套接字。

与UDP不同,两个具有不同源IP地址或源端口号的到达TCP报文段将被定向到两个不同的套接字,除非TCP报文段携带了初始创建连接的请求。

TCP客户—服务器编程的例子:

  • TCP服务器应用程序有一个"欢迎套接字",它在12000号端口上等待来自TCP客户的连接建立请求。
  • TCP客户使用下面的代码创建一个套接字并发送一个连接建立请求报文段
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName,12000))
  • 一条连接建立请求只不过是一个目的端口号为12000,TCP首部的特定"连接建立位"置位的TCP报文段。这个报文段包含一个由客户选择的源端口号。
  • 当运行服务器进程的计算机的主机操作系统接收到具有目的端口12000的入连接请求报文段后,它就定位服务器进程,该进程正在端口号12000等待接收连接。
    该服务器进程则创建一个新的套接字
connectionSocket, addr = serverSocket.accept()
  • 该服务器的运输层还注意到连接请求报文段中的下列4个值:

      1.该报文段中的源端口号
      2.源主机IP地址
      3.该报文段中的目的端口号
      4.自身的IP地址
    

新创建的连接套接字通过这4个值来标识。
所有后续到达的报文段,如它们的源端口号,源主机IP地址,目的端口号,目的IP地址都与这4个值匹配,则被分解到这个套接字。
随着TCP连接完成,客户和服务器便可相互发送数据了。

服务器主机可支持很多并行的TCP套接字,每个套接字与一个进程相联系,由其四元组来标识每个套接字。
当一个TCP报文段到达主机时,所有4个字段被用来将报文段定向到相应的套接字。
在这里插入图片描述

主机C向服务器B发起了两个HTTP会话,
主机A向服务器B发起了一个HTTP会话。
主机A与主机C及服务器B都有自己唯一的IP地址,分别是A,C,B。
主机C为其两个HTTP连接分配了两个不同的源端口号(26145 和 7532)。
主机A选择源端口号时与主机C不相干,
因此它也可将源端口号26145分配给其HTTP连接,
但这不是问题,即服务器B仍能分清这两个具有相同源端口号的连接。

3. Web服务器与TCP

考虑一台运行Web服务器的主机,如在端口80运行一个Apache Web服务器。
当客户(如浏览器)向该服务器发送报文段时,所有报文端的目的端口都将为80。
初始建立连接报文段和承载HTTP请求的报文段都有80的目的端口。
该服务器能根据源IP地址和源端口号来区分来自不同客户的报文段。

Web服务器为每条连接生成一个新进程。
每个这样的进程都有自己的连接套接字,通过这些套接字可收到HTTP请求和发送HTTP响应。

连接套接字和进程之间并非总是有着一一对应的关系。
如今的高性能Web服务器通常只使用一个进程,但为每个新的客户连接创建一个具有新连接套接字的新线程。

如客户与服务器使用持续HTTP,则在整条连接持续期间,客户与服务器之间经由同一个服务器套接字交换HTTP报文。
如客户与服务器使用非持续HTTP,则对每一对请求/响应都创建一个新的TCP连接并在随后关闭。

频繁的创建和关闭套接字会影响一个Web服务器的性能。

无连接运输:UDP

如你对设计一个不提供不必要服务的最简化的运输层协议,你将打算怎样做?

运输层最低限度必须提供一种复用/分解服务,以便在网络层与正确的应用级进程之间传递数据。

UDP,除了复用/分解功能及少量的差错检测外,它几乎没有对IP增加别的东西。
UDP从应用进程得到数据,附加上用于多路复用/分解服务的源和目的端口号字段,以及两个其他的小字段,然后将形成的报文段交给网络层。

网络层将该运输层报文段封装到一个IP数据报中,然后尽力而为地尝试将此报文段交付给接收主机

如到达接收主机,UDP使用目的端口号将报文段中数据交付给正确的应用进程。
使用UDP在发送报文段前,发送方和接收方运输层实体间没有握手。
DNS是一个通常使用UDP的应用层协议例子。

许多应用更适合UDP,原因如下:

  • 发送什么数据及何时发送的应用层控制更为精细。

    采用UDP时,只要应用进程将数据传递给UDP,
    UDP就将此数据打包进UDP报文段,并立即将其传递给网络层。
    TCP有一个拥塞控制机制,以便源和目的主机间一条或多条链路变得极度拥塞时来遏制运输层TCP发送方。
    TCP仍将继续重新发送数据报文直到目的主机收到此报文并加以确认,无论时间。
    实时应用通常要求最小的发送速率,不希望过分延迟报文段发送,且能容忍一些数据丢失,更适合UDP。
    应用一部分可用来实现一些额外的服务。

  • 无须连接建立
    连接建立时延。

  • 无连接状态
    TCP需在端系统中维护连接状态,此连接状态包括接收和发送缓存,拥塞控制参数,序号,确认号的参数。
    UDP不维护连接状态,也不跟踪这些参数。

  • 分组首部开销小
    每个TCP报文段有20字节的首部开销,UDP仅有8字节。
    在这里插入图片描述使用UDP的应用是可能实现可靠数据传输的。
    可通过在应用程序自身中建立可靠性机制来完成。
    谷歌的Chrome浏览器使用QUIC协议,在UDP之上的应用层协议中实现可靠。

UDP报文段结构

UDP报文段结构如下图所示,它由 RFC 768 定义。
应用层数据占用UDP报文段的数据字段。

在这里插入图片描述

UDP检验和

UDP检验和提供了差错检测功能。
即,检验和用于确定当UDP报文段从源到达目的地移动时,其中的比特是否发生了改变(例如,由于链路中的噪声干扰或者存储在路由器中时引入问题)。

发送方的UDP对报文段中的所有16比特字的和进行反码运算,求和时遇到的任何溢出都被回卷。
得到的结果放入UDP报文段的检验和字段。

例:假定有下面3个16比特的字:

0110 0110 0110 0000
0101 0101 0101 0101
1000 1111 0000 1100

这些16比特字前两个之和是:

0110 0110 0110 0000
0101 0101 0101 0101
————————————————————
1011 1011 1011 0101

再将上面的和与第三个字相加

1011 1011 1011 0101
1000 1111 0000 1100
————————————————————
0100 1010 1100 0010

注意最后一次加法有溢出,它要被回卷。
反码运算就是所有的0换成1,所有的1换成0。

	0100 1010 1100 0010
反码		
	1011 0101 0011 1101

这就变为了检验和。

在接收方,全部的4个16比特字(包括检验和)加在一起。
如该分组中没有引入差错,则显然在接收方处该和将是1111 1111 1111 1111 。

为什么UDP首先提供了检验和?

像许多链路层协议(包括流行的以太网协议)也提供了差错检测那样。
其原因是不能保证源和目的之间的所有链路都提供差错检测。
就是说,也许这些链路中的一条可能使用没有差错检测的协议。
此外,即使报文段经链路正确地传输,当报文段存储在某台路由器的内存中时,也可能引入比特差错。

在既无法确保链路可靠性,又无法确保内存中的差错检测情况下,如端到端数据传输服务要提供差错检测,UDP必须在端到端基础上在运输层提供差错检测。

在系统设计中被称作端到端原则的例子。
原则表述为因为某种功能(此时为差错检测)必须基于端到端实现:
“与在较高级别提供这些功能的代价相比,在较低级别上设置的功能可能是冗余或几乎没有价值的”。

可靠数据传输原理

为上层实体提供的服务抽象是:
数据可以通过一条可靠信道进行传输。借助于可靠信道,传输数据比特不会受到损坏(由0变为1,或者相反)或丢失,而且所有数据都按照其发送顺序交付。

在这里插入图片描述
实现这种服务抽象是可靠数据传输协议(reliable data transfer protocal)的责任。

考虑到底层信道模型越来越复杂,将不断开发一个可靠数据传输协议的发送方一侧和接收方一侧。

底层信道将不会对分组重排序。

仅考虑单向数据传输(unidirectional data transfer)的情况,即数据传输是从发送端到接收端的。

构造可靠数据传输协议

1. 经完全可靠信道的可靠数据传输:rdt1.0

考虑最简单的情况,即底层信道是完全可靠的。
称该协议为 rdt1.0 ,该协议本身是简单的。

图中显示了 rdt1.0 发送方和接收方的有限状态机(Finite-State Machine, FSM)的定义。
在这里插入图片描述
发送方和接收方有各自的FSM。
引起变迁的事件显示在表示变迁的横线上方,事件发生时采取的动作显示在横线下方。

如对一个事件没有动作,或没有就事件发生而采取了一个动作,我们将在横线上方或下方使用符号A,以分别明确地表示缺少动作或事件

FSM的初始状态用虚线表示。

rdt的发送端只通过rdt_send(data)事件接受来自较高层的数据,产生一个包含该数据的分组(经由make_pkt(data)),并将分组发送到信道中。
在接收端, rdt 通过 rdt_rcv(packet) 事件从底层信道接收一个分组,从分组中取出数据(经由 extract(packet, data)动作),并将数据上传给较高层(通过deliver_data(data))。
实际上,rdt_rcv(packet) 事件由较低层协议的过程调用产生(如rdt_rcv())。
也假定接收方接收数据速率能与发送方发送数据速率一样块。

2. 经具有比特差错信道的可靠数据传输:rdt2.0

底层信道更为实际的模型是分组中的比特可能受损的模型。
在分组的传输、传播或缓存的过程中,这种比特差错通常会出现在网络的物理部件中。

继续假定所有发送的分组(虽然有些比特可能受损)将按其发送的顺序被接收。

基于重传机制的可靠数据传输协议称为自动重传请求(Automatic Repeat reQuest, ARQ)协议

重要的是,ARQ协议中还需要另外三种协议功能来处理存在比特差错的情况:

  • 差错检测
    需要一种机制使接收方检测到何时出现了比特差错。
  • 接收方反馈
    ACK:肯定确认
    NAK:否定确认
  • 重传
    接收方收到有差错的分组时,发送方将重传该分组

在这里插入图片描述
发送方将不会发送一块新数据,除非发送方确信接收方已正确接收当前分组。由于这种行为,rdt2.0这样的协议被称为停等(stop-and-wait)协议。

上述协议一个没有考虑的点:
实际上,ACK或NAK分组也可能在传输中受损。

考虑处理受损ACK和NAK时的3种可能性:

  • 对于第一种可能性,考虑在口述报文情况下人可能的做法。
  • 第二种可能是增加足够的建议和比特,使发送方不仅可以检查差错,还可恢复差错。
  • 第三种方法是,当发送方收到含糊不清的ACK或NAK分组时,只需重传当前数据分组即可。这种方法在发送方到接收方的信道中引入了冗余分组(duplicate packet)。

在数据分组中添加一新字段,让发送方对其数据分组编号,即将发送数据分组的 序号(sequence number)放在该字段

协议rdt2.1使用了从接收方到发送方的肯定确认和否定确认.
当接收到失序的分组时,接收方对收到的分组发送一个肯定确认。
如果收到受损的分组,则接收方发送一个否定确认。

rdt2.2在有比特差错信道上实现的一个无NAK的可靠数据传输协议。

rdt2.1和rdt2.2的细微变化在于,接收方此时必须包括由一个ACK报文所确认的分组序号(这可以通过在接收方FSM中,在make_pkt()中包括参数 ACK 0 或 ACK 1 来实现),发送方此时必须检查接收到的ACK报文中被确认的分组序号(这可通过在发送方 FSM中,在 isACK()中包括参数 0 或 1 来实现)。

在这里插入图片描述在这里插入图片描述
2.1版本相比2.0版本,做出的改变:

  • 考虑到了ACK和NAK和数据分组一样,可能在传输过程被损坏.[对ACK依然添加检验和]
  • 考虑到了冗余分组下,接收方如何区分冗余分组,保证按序交付及单一交付问题[对数据分组添加序号]

在这里插入图片描述在这里插入图片描述
3. 经具有比特差错的丢包信道的可靠数据传输:rdt3.0

现在假定除了比特受损外,底层信道还会丢包。
怎样检测丢包以及丢包发生后该如何处理。
在rdt2.2,使用检验和,序号,ACK分组,重传等,使我们能给出后一个问题的答案。

有很多种方法用于解决丢包问题。
这里,让发送方负责检测和恢复丢包。假定发送方传输一个数据分组,该分组或者接收方对该分组的ACK发生了丢失。
在这两种情况下,发送方都收不到应当到来的接收方的响应。如果发送方愿意等待足够长时间以便确定分组已丢失,则它只需重传该数据分组。

发送方等待的时间至少为:
发送方和接收方之间的一个往返时延(可能会包括在中间路由器缓冲时延)+接收方处理一个分组所需的时间。

理想的协议应尽可能快地从丢包中恢复出来。
等待一个最坏情况的时延可能意味着要等待一段较长的时间,直到启动差错恢复为止。
因此,实践中采取的方法是发送方明智地选择一个时间值,以判定可能发生了丢包(尽管不能确保)。如果在此时间内,没收到ACK,则重传该分组。

如果一个分组经历了一个特别大的时延,发送方可能会重传该分组,即使该分组数据及其ACK都没有丢失。
这就在发送方到接收方的信道中引入了冗余数据分组(duplicate data packet)的可能性。

rdt2.2的序号可处理冗余分组。

发送方不知道是一个数据分组丢失,还是一个ACK丢失或者只是该分组或ACK过度延时。在所有这些情况下,动作是同样的:重传

为实现基于时间的重传机制,需要一个倒计数定时器(countdown timer),在一个给定的时间量过期后,可中断发送方。
因此,发送方需要能做到:

  • 每次发送一个分组(包括第一次分组和重传分组)时,便启动一个定时器。
  • 响应定时器中断(采取适当的动作)
  • 终止定时器

在这里插入图片描述

下图显示了在没有丢包和延迟分组情况下协议运作的情况,以及它是如何处理数据分组丢失的。

在图中,时间从图的顶部朝底部移动;
一个分组的接收时间必定迟于一个分组的发送时间,这是因为发送时延与传播时延的原因。
在图中b~d 中,发送方括号部分表明了定时器的设置时刻以及随后的超时。

因为分组序号在0和1之间交替,因此 rdt3.0 有时被称为比特交替协议(alter-nating-bit protocal)。

在这里插入图片描述
数据传输协议的要点,在检验和、序号、定时器、肯定和否定确认分组这些技术中,每种机制都在协议的运行找那个起到了必不可少的作用。至此,我们得到了一个可靠数据传输协议!

流水线可靠数据传输协议

rdt3.0性能问题的核心在于它是一个停等协议。

考虑一种具有两台主机的理想化场合,一台主机位于美国西海岸,另一台位于美国东海岸。
在这两个端系统之间的光速往返传播时延RTT大约为30毫秒。假定彼此通过一条发送速率R为1Gbps(每秒10^9比特)的信道相连。包括首部字段和数据的分组长L为1000字节(8000比特),发送一个分组进入1Gbps链路实际所需时间是:
在这里插入图片描述
在这里插入图片描述图中显示了对于该停等协议,如发送方在t=0开始发分组,则在t=L/R=8us后,最后1比特数据进入发送端信道。该分组经过15ms的穿越国家的旅途后到达接收端,
该分组的最后1比特在时刻t = RTT/2 + L/R=15.008ms时到达接收方。
为简化,设ACK分组很小(以便可忽略其发送时间),接收方一旦收到一个数据分组的最后 1 比特后立即发送ACK,ACK在时刻 t=RTT+L/R=30.008 时在发送方出现。
此时,发送方可发送下一个报文。
因此,在30.008ms内,发送方的发送只用了0.008ms。

如果我们定义发送方(或信道)的利用率(utilization)为:
发送方实际忙于将发送比特送进信道的那部分时间与发送时间之比,
停等协议有着非常低的发送方利用率 U s e n d e r U_{sender} Usender
U s e n d e r = ( L / R ) / ( R T T + L / R ) = 0.008 / 30.008 = 0.00027 U_{sender}=(L/R) / (RTT+L/R) = 0.008/30.008 = 0.000 27 Usender=(L/R)/(RTT+L/R)=0.008/30.008=0.00027

从其他角度看,发送方在30.008ms内只能发送1000字节,有效的吞吐量仅为267kbps,即使有 1Gbps的链路可用也是如此!

这种特殊性能的解决方法是:
不以停等方式运行,允许发送方发送多个分组而无须等待确认。
因为许多从发送方向接收方输送的分组可被看成是填充到一条流水线中,故这种技术被称为流水线(pipelinging)。

在这里插入图片描述
流水线技术对可靠数据传输协议可带来如下影响:

  • 必须增加序号范围,因为每个输送中的分组(不计算重传的)必须有一个唯一的序号,且有许多在输送中的未确认报文。
  • 协议的发送方和接收方两端也许不得不缓存多个分组。发送方最低限度应能缓存那些已发送但没有确认的分组。
    接收方或许也需要缓存那些已正确接收的分组。
  • 所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失,损坏,延时过大的分组。
    解决流水线的差错恢复有两种基本方法是:
    回退N步(Go-Back-N, GBN),选择重传(Selective Repeat, SR)。

回退N步

允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。

在这里插入图片描述

上图显示了发送方看到的GBN协议的序号范围。
如我们将基序号定义为最早未确认分组的序号,
将下一个序号定义为最小的未使用序号(即下一个待发分组的序号)
则可将序号范围分割成4段。

在 [0, base-1] 段内的序号对应于已经发送并被确认的分组。
[base, nextseqnum-1] 段内对应已经发送但未被确认的分组。
[nextseqnum, base+N-1]段内的序号用于那些要被立即发送的分组,如有数据来自上层的话。

大于或等于 base+N 的序号是不能使用,直到当前流水线中未被确认的分组(特别是序号为base的分组)已得到确认为止。

那些已被发送但还未被确认的分组的许可序号范围可被看成是一个在序号范围内长度为N的窗口。
随着协议运行,该窗口在序号空间向前滑动。

因此,N常被称为窗口长度(window size)。
GBN协议也常被称为滑动窗口协议(sliding-window protocol)。

为何要限制被发送的,未被确认的分组数目为N?
流量控制是发送方施加限制的原因之一。

在实践中,一个分组的序号承载在分组首部的一个固定长度的字段中。
如果分组序号字段的比特数是k,则该序号范围是 [ 0 , 2 k − 1 ] [0, 2^k-1] [0,2k1]
在一个有限的序号范围内,所有涉及序号的运算必须使用模 2 k 2^k 2k运算。
即序号空间可被看作是一个长度为 2 k 2^k 2k的环,其中序号 2 k − 1 2^k-1 2k1紧接着序号0。

rdt3.0有一个 1 比特的序号,序号范围是[0, 1]。
TCP有一个 32 比特的序号字段,其中的TCP序号是按字节流中的字节进行计数的,而不是按分组计数。

下图给出了一个基于ACK,无NAK的GBN协议的发送方和接收方这两端的扩展FSM描述。
称该FSM描述为扩展FSM,是因为我们已经增加了变量base和nextseqnum,还增加了对这些变量的操作及与这些变量有关的条件动作。
在这里插入图片描述在这里插入图片描述GBN发送方必须响应三类型事件:

  • 上层的调用。
    当上层调 rdt_send() 分组时,发送方先检查发送窗口是否已满,即是否有N个已发送但未被确认的分组。
    如果窗口未满,则产生一个分组并将其发送,并相应地更新变量。
    如果窗口已满,发送方只需将数据返回给上层,隐式地指示上层该窗口已满。

    上层可能稍后重试。在实际实现中,发送方更可能缓存(并不立即发送)这些数据,或者使用同步机制(如一个信号量或标志)允许上层仅当窗口不满时才调用rdt_send() 。

  • 收到一个ACK。
    在GBN,对序号为n的分组的确认采取累计确认(cumulative acknowledgment)的方式,表明接收方已正确接收序号为n的以前且包括n的所有分组。

  • 超时事件。
    如出现超时,发送方重传所有已发送但还未被确认过的分组。
    上图中,发送方仅使用一个定时器,可被当作是最早的已发送但未被确认的分组所使用的定时器。
    如收到一个ACK,但仍有已发送但未被确认的分组,则定时器被重新启动。
    如没有已发送但未被确认的分组,停止定时器。

在GBN,接收方的动作也很简单:

如一个序号为n的分组被正确接收到,且按序(即上次交付给上层的数据是序号为n-1的分组),则接收方为分组n发送一个ACK,并将该分组中的数据部分交付到上层。

在所有其他情况下,接收方丢弃该分组,并为最近按序接收的分组重新发送ACK。
注意到因为一次交付给上层一个分组,如果分组k已接收并交付,则所有序号比k小的分组也已经交付。
因此,累计确认是GBN一个自然的选择。

在GBN协议中,接收方丢弃所有失序分组。

尽管丢弃一个正确接收(但失序)的分组有点浪费,这样做也有理由。
接收方必须按序将数据交付上层。

现假定期望接收分组n,而分组n+1却到了。
因为数据必须按序交付,接收方可能缓存(保存)分组n+1,然后,在它收到并交付分组n后,再将该分组交付到上层。

如分组n丢失,则该分组及分组n+1最终将在发送方根据GBN重传规则而被重传。
因此,接收方只需丢弃分组n+1即可。

这种方法的优点是接收缓存简单,即接收方不需要缓存任何失序分组。
虽然发送方必须维护窗口的上下边界及 nextseqnum 在该窗口中的位置,但接收方需维护的唯一信息就是下一个按序接收的分组的序号。

该值保存在expectedseqnum变量,如图3-21中接收方FSM所示。
当然,丢弃一个正确接收的分组的缺点是随后对该分组的重传也许会丢失或出错,因此甚至需要更多的重传。
在这里插入图片描述上图中应该是发送方发送的每个数据分组拥有一个独立的定时器.

窗口长度为4个分组的GBN协议执行情况。

基于事件的编程(event-based programming)方式中,这些过程要么被协议栈中的其他过程调用,要么作为一次中断的结果。

在发送方,这些事件包括:

  • 来自上层实体的调用去调用rdt_send()
  • 定时器中断
  • 报文到达时,来自下层的调用去调用rdt_rcv

这些技术综合使用了序号,累计确认,检验和,超时/重传

选择重传

GBN潜在地允许发送方用多个分组"填充流水线",因此避免了停等协议中所提到的信道利用率问题。

GBN本身也存在性能问题。
尤其当窗口长度和带宽时延积都很大时,在流水线中有很多分组更是如此。
单个分组的差错就能引起GBN重传大量分组,许多分组根本没必要重传。
随着信道差错率的增加,流水线可能会被这些不必要重传的分组所充斥。

选择重传(SR)协议通过让发送方仅重传那些它怀疑在接收方出错(即丢失或受损)的分组,而避免了不必要的重传。
这种个别的,按需的重传要求接收方逐个地确认正确接收的分组。
再次用窗口长度N来限制流水线中未完成,未被确认的分组数。
然而,与GBN不同的是,发送方已经收到了对窗口中某些分组的ACK。

在这里插入图片描述
SR接收方将确认一个正确接收的分组而不管其是否按序。
失序的分组将被缓存直到所有丢失分组(序号更小的分组)皆被收到为止。这时才可将一批分组按序交付给上层。

SR发送方的事件与动作:

  • 从上层收到数据。
    当从上层接收到数据后,SR发送方检查下一个可用于该分组的序号。
    如序号位于发送方的窗口内,则将数据打包并发送。
    否则,就像在GBN,要么将数据缓存,要么将其返回给上层以便后续传输。
  • 超时。
    定时器再次被用来防止丢失分组。
    然而,现在每个分组必须拥有自己的逻辑定时器,因为超时发生后只能发送一个分组。
    可使用单个硬件定时器模拟多个逻辑定时器。
  • 收到ACK。
    如收到ACK,倘若该分组序号在窗口内,则SR发送方将那个被确认的分组标记为已接收。
    如该分组的序号等于send_base,则窗口基序号向前移动到具有最小序号的未确认分组处。
    如窗口移动了且有序号落在窗口内的未发送分组,则发送这些分组。

SR接收方的事件与动作:

  • 序号在[rcv_base, rcv_base+N-1]内的分组被正确接收。
    此情况下,收到的分组落在接收方的窗口内,一个选择ACK被回送给发送方。
    如该分组以前没收到过,则缓存该分组。
    如该分组的序号等于接收窗口的基序号,则该分组及以前缓存的序号连续的(起始于rcv_base)分组交付给上层。
    然后,接收窗口按向前移动分组的编号向上交付这些分组。
    举例:
    下图收到一个序号为rcv_base=2的分组,该分组及分组3/4/5可被交付给上层。
  • 序号在[rcv_base-N, rcv_base-1]内的分组被正确收到。
    产生一个ACK,即使该分组是接收方以前已确认过的分组。
  • 其他情况。
    忽略该分组

在这里插入图片描述注意到SR接收方的事件与动作中第二步很重要,接收方重新确认已收到过的那些序号小于当前窗口基序号的分组。
应该理解这种重新确认分组确实是需要的。
例:
给定下图所示的发送方和接收方的序号空间,如分组send_base的ACK没有从接收方传播回发送方,则发送方最终将重传分组send_base。即使显然(对我们而非发送方来说),接收方已经收到了该分组。
如接收方不确认该分组,则发送方窗口将永远不能向前滑动。
这例子说明了SR协议的一个重要方面。
对于哪些分组已经被正确接收,哪些没有,发送方和接收方并不总是能看到相同的结果。
对SR协议而言,这就意味着发送方和接收方的窗口并不总是一致。

在这里插入图片描述
当我们面对有限序号范围的现实时,发送方和接收方窗口间缺乏同步会产生严重的后果。

考虑下面例子:

包括4个分组序号0,1,2,3的有限序号范围且窗口长度为3.
假定发送了分组0至2,并在接收方被正确接收且确认了。
此时,接收方窗口落在第4,5,6个分组上,其序号分别为3,0,1。

现在考虑两种情况。

在第一种情况下,
如上图a所示,对前3个分组的ACK丢失,因此发送方重传这些分组。
因此,接收方下一步要接收序号为0的分组。

在第二种情况下,
如上图b所示,对前3个分组的ACK都被正确交付。
因此,发送方向前移动并发送第4,5,6个分组,其序号分别为3,0,1。
序号为3的分组丢失但序号为0的分组到达(一个包含新数据的分组)。

接收方所能观察到的是它从信道中收到的以及它向信道中发出报文序列
没办法区分,是第一个分组的重传还是第五个分组的初次传输。
显然,窗口长度比序号空间小1时,协议无法工作。

窗口必须是多少?
窗口长度必须小于或等于序号空间大小的一半。

可靠数据传输机制及其用途的总结

机制用途和说明
检验和用于检测在一个传输分组中的比特错误。[检测传输比特位错误]
定时器用于超时/重传一个分组。[丢包,重传]
序号用于为从发送方流行接收方的数据分组按顺序编号。[区分冗余分组,有序且不重复的交付]
确认接收方用于告诉发送方一个分组或一组分组已被正确地接收了。确认可是逐个或累积。[可靠交付--预期的数据分组被无差错的正确接收]
否定确认接收方用于告诉发送方某个分组未被正确地接收。
窗口,流水线发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,提供发送方利用率。[提供效率]

考虑在底层信道模型中的一个遗留假设来结束对可靠数据传输协议的讨论。
前面曾假定分组在发送方与接收方之间的信道中不能被重新排序。
这在发送方与接收方由单段物理线路相连下,通常是合理的。

当连接两端的 “信道” 是一个网络时,分组重新排序可能会发生。
分组重新排序的一个表现是,一个具有序号或确认号 x 的分组的旧副本可能会出现,即使发送方或接收方的窗口中都没有包含x。
对分组重新排序,信道可被看成基本上是在缓存分组,并在将来任意时刻自然地释放出这些分组。

由于序号可被重新使用,需小心,以免出现这样的冗余分组。
实际应用中的方法是,确保一个序号不被重新使用,直到发送方 “确信” 任何先前发送的序号为 x 的分组都不再在网络中为止。

通过假定一个分组在网络中 “存活” 时间不会超过某个固定最大时间量来做到这一点。
在高速网络的TCP扩展中,最长的分组寿命被假定为大约3分钟。

面向连接的运输:TCP

TCP是因特网运输层的面向连接的可靠的运输协议。

为了提供可靠数据传输,TCP依赖于前一节所讨论的许多基本原理,包括差错检测,重传,累积确认,定时器及用于序号和确认号的首部字段。

TCP连接

TCP被称为面向连接的(connection-oriented)。

这是因为,在一个应用进程可开始向另一个应用进程发送数据前,这两个进程必须先互相 “握手” ,即它们必须相互发送某些预备报文段,以建立确保数据传输的参数。

作为TCP连接建立的一部分,连接的双方都将初始化与TCP连接相关的许多TCP状态变量。

这种TCP “连接” 不是一条像在电路交换网络中的端到端TDM或FDM电路。
相反,该 “连接” 是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP程序中。

TCP协议只在端系统中运行,而不再中间的网络元素(路由器和链路层交换机)中运行,所以中间的网络元素不会维持TCP连接状态。

中间路由器看到的是数据报,不是连接。

TCP连接提供的是全双工服务(full-duplex service):
如一台主机的进程A与另一主机的进程B存在一TCP连接,那么应用层数据可在从进程B流向进程A的同时,
也从进程A流向进程B。

TCP连接总是点对点(point-to-point)的,即在单个发送方与单个接收方之间的连接。
所谓 “多播” ,即在一次发送操作中,从一个发送方将数据传送给多个接收方。

TCP是怎样建立连接的。

假设运行在某台主机上的一个进程想与另一台主机上的一个进程建立一条连接,发起连接的进程称为客户进程,另一个进程被称为服务器进程。

客户进程先要通知客户运输层,它想与服务器上的一个进程建立一条连接。

客户上的TCP便开始与服务器上的TCP建立一条TCP连接。

客户先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应,最后,客户再用第三个特殊报文段作为响应。

前两个报文段不承载“有效载荷”,也就是不包含应用层数据;而第三个报文段可承载有效载荷。
由于在两个主机间发送了3个报文段,所以这种连接建立过程常称为三次握手(three-way handshake)。

考虑,从客户进程向服务器进程发送数据,客户进程通过套接字传递数据流,数据一旦通过该门,就由客户中运行的TCP控制了。
TCP将这些数据引导到该连接的发送缓存,发送缓存是发起三次握手期间设置的缓存之一。

接下来,TCP会不时从发送缓存里取出一块数据,将数据传递到网络层。
在TCP规范中没提及TCP应何时实际发送缓存里的数据,指示描述为"TCP应该在它方便的时候以报文段的形式发送数据"。

TCP可从缓存中取出并放入报文段中的数据数量受限于最大报文段长度(MSS)。
MSS常根据最初确定的由本地发送主机发送的最大链路层帧长度(即所谓的最大传输单元(Maximum Transmission Unit, MTU))来设置。
设置该MSS要保证一个TCP报文段加上TCP/IP首部长度将适合单个链路层帧。
在这里插入图片描述
TCP连接的组成包括:
一台主机上的缓存,变量和与进程连接的套接字,以及另一台主机上的缓存,变量,进程连接套接字。

TCP报文段结构

TCP报文段由首部字段和一个数据字段组成。
数据字段包含一块应用数据。
MSS限制了报文段数据字段的最大长度。
当TCP发送一个大文件,TCP通常将该文件划分成长度为MSS的若干块(最后一块除外,它通常小于MSS)。

交互式应用通常传送长度小于MSS的数据块。

在这里插入图片描述
首部包含 源端口号目的端口号,它被用于多路复用/分解来自或送到上层应用的数据。
另外,也包括检验和字段(checksum field)。

TCP报文段首部还包含下列字段:

  • 32比特的序号字段和32比特的确认号字段。
  • 16比特的接收窗口字段,用于流量控制,用于指示接收方愿意接收的字节数量。
  • 4比特的首部长度字段,指示了以32比特的字为单位的TCP首部长度。由于TCP选项字段原因,TCP首部的长度是可变的。
  • 可选与变长的选项字段,用于发送方与接收方协商最大报文段长度,或在高速网络环境下用作窗口调节因子时使用。
  • 6比特的标志字段。
    ACK比特用于指示确认字段中的值是有效的。
    RST,SYN,FIN比特用于连接建立和拆除。
    在明确拥塞通告中使用了CWR和ECE比特。
    当PSH比特被置位时,就指示接收方应立即将数据交给上层。
    URG比特用来指示报文段里存在着被发送端的上层实体置为"紧急"的数据。
    紧急数据的最后一个字节由16比特的紧急数据指针字段指出。
    紧急数据存在并给出指向紧急数据尾指针时候,TCP必须通知接收端的上层实体。

1. 序号和确认号

一个报文段的序号是该报文段首字节的字节流编号。
举例:
设主机A的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流,
主机A中的TCP将隐式地对数据流中的每一个字节编号。
设数据流含500 000字节的文件组成,
其MSS为1000字节,
数据流的首字节编号是0
下图,该TCP将为数据流构建500个报文段。
给第一个报文段分配序号0
第二个报文段分配序号1000
以此类推
每个序号被填入相应TCP报文段首部的序号字段中

在这里插入图片描述
从主机B到达的每个报文段中有一个序号用于从B流向A的数据
主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的编号。
假主机A已收到来自主机B的编号为0~535的所有字节,
同时它打算发一个报文段给主机B
主机A等待主机B的数据流中字节536及之后的所有字节
主机A会在它发往主机B的报文段的确认号字段中填上536。

设主机A已收到一个来自主机B的包含字节0~535的报文段,
及另一个包含字节900~1000的报文段。
由于某种原因,主机A还没收到字节536~899,
主机A仍在等待字节536及其后的字节。
因此,A到B的下一个报文段将在确认号字段中包含536,
TCP是累计确认。

Telnet:序号和确认号的一个学习案例

Telnet由FRC 854定义,是一个用于远程登录的流行应用层协议。
运行在TCP之上,被设计成可在任意一对主机间工作。

设主机A发起一个与主机B的Telnet会话(因为主机A发起会话,被标记为客户; 主机B被标记为服务器);
在A键入的每个字符会被发送至远程主机;
远程主机将回送每个字符的副本给客户,并将这些字符显示在Telnet用户的屏幕上

假设用户输入字符’C’,
设客户和服务器起始序号分别是42和79。
客户发送的第一个报文段的序号是42,服务器发送的第一个报文段的序号是79。
确认号是主机正在等待的数据的下一字节序号。
TCP连接建立但没发送数据前,该客户等待字节79,服务器等待字节42。

在这里插入图片描述

共发送3个报文段:
第一个报文段由客户发往服务器。
	在它的数据字段包含一字节的字符‘C’的ASCII码。
	(第一个报文段序号字段为42,确认号字段是79)。

第二个报文段由服务器发往客户。
	它有两个目的:首先它是为该服务器所收到数据提供一个确认,
	(在确认字段填入43,服务器高速客户它已经成功收到字节42及以前的所有字节,正等待字节43的出现)
	第二个目的是回显字符‘C’。
	(第二字段的数据字段填入‘C’,报文序号是79(即从服务器到客户端的数据流的起始序号,也是服务器要发送的第一个字节的数据))

第三个报文段是客户发往服务器的。
	它的唯一目的是确认已从服务器收到的数据。
	(该报文段数据字段为空,确认字段是80(因客户已收到字节流中序号为79及以前的字节))

对客户到服务器的数据确认被装载在一个承载服务器到客户的数据的报文段中,这种确认被称为是被捎带(piggybacked)在服务器到客户的数据报文段中的。

为什么报文段里没有数据还仍有序号: 这是因为TCP存在序号字段,报文段需要填入某个序号。

往返时间的估计与超时

TCP采用超时/重传机制来处理报文段的丢失问题。

最明显的一个问题是超时间隔长度的设置。

超时间隔必须大于该连接的往返时间(RTT),即从一个报文段发出到它被确认的时间。

1. 估计往返时间

报文段的样本RTT(表示为 SampleRTT)就是从某报文段被发出(即交给IP)到对该报文段的确认被收到之间的到时间量。

大多数TCP的实现仅在某个时刻做一次 SampleRTT 测量。
也即,任意时刻,仅为一个已发送的但目前尚未被确认的报文段估计SampleRTT,从而产生一个接近每个RTT的新SampleRTT值。

为了估计一个典型的RTT,要采取某种对SampleRTT取平均的方法。
TCP维持一个SampleRTT均值(称为 EatimatedRTT。

一旦获得一个新SampleRTT,TCP就会依据下列公式来更新EstimatedRTT:

E s t i m a t e d R T T = ( 1 − α ) ∗ E s t i m a t e d R T T + α ∗ S a m p l e R T T EstimatedRTT = (1-\alpha)*EstimatedRTT + \alpha*SampleRTT EstimatedRTT=(1α)EstimatedRTT+αSampleRTT
α \alpha α的推荐值是 α \alpha α =0.125(即 1/8 )。

RTT偏差DevRTT,用于估算SampleRTT一般会偏离EstimatedRTT的程度:

D e v R T T = ( 1 − β ) ∗ D e v R T T + β ∗ ∣ S a m p l e R T T − E s t i m a t e d R T T ∣ DevRTT=(1-\beta)*DevRTT +\beta* | SampleRTT - EstimatedRTT| DevRTT=(1β)DevRTT+βSampleRTTEstimatedRTT
β \beta β 的推荐值为0.25 。

2. 设置和管理重传超时间隔

超时间隔设为EstimatedRTT加上一定余量。
当SampleRTT波动大时,余量应该大些。波动小时,可余量应该小些。

在TCP的确定重传超时间隔的方法中,所有这些因素都考虑到了:
T i m e o u t I n t e r v a l = E s t i m a t e d R T T + 4 ∗ D e v R T T TimeoutInterval = EstimatedRTT + 4*DevRTT TimeoutInterval=EstimatedRTT+4DevRTT
推荐的初始 TimeoutInterval 值为1秒。
出现超时后,TimeoutInterval 值将加倍。

只要收到报文段并更新了EstimatedRTT,就使用上述公式再次计算 TimeoutInterval 。

可靠数据传输

TCP在IP不可靠的尽力而为服务上创建了一种可靠数据传输服务(reliable data transfer service)。

TCP的可靠数据传输服务确保一个进程从其接收缓存读出的数据流是无损坏,无间隙,非冗余,按序的数据流;即该字节流与连接的另一端系统发送出的字节流是完全相同。

推荐的定时器管理过程 [RFC 6298] 仅使用单一的重传定时器,即使有多个已发送但还未被确认的报文段。

以两个递增的步骤讨论TCP如何提供可靠数据传输。
先给出一个TCP发送方高度简化的描述,该发送方只用超时来恢复报文段的丢失;然后再给出一个更全面的描述,该描述中除了使用超时机制外,还使用冗余确认技术。

如下给出一个TCP发送方高度简化的描述。
TCP发送方有3个与发送和重传有关的事件:

从上层应用程序接收数据;
定时器超时;
收到ACK。
/* 假设发送方不受TCP流量和拥塞控制的限制,来自上层数据的长度小于MSS,且数据传输只在一个方向进行。*/

NextSeqNum=InitialSeqNumber
SendBase=InitialSeqNumber
loop (永远) {
		switch(事件)
			事件:从上面应用程序接收到数据e
				生成具有序号 NextSwqNum 的TCP报文段
				if (定时器当前没有运行)
					启动定时器
				向IP传递报文段
				NextSeqNum=NextSeqNum+length(data)
				break;
			事件:定时器超时
				重传具有最小序号但仍未应答的报文段
				启动定时器
				break;
				
			事件:收到 ACK,具有ACK字段值 y
				if (y > SendBase) {
					SendBase=y
					if (当前仍无任何应答报文段)
						启动定时器
				}
				break;
	} /* 结束永远循环 */```

第二个主要事件是超时。TCP通过重传引起超时的报文段来响应超时事件。然后TCP重启定时器。

TCP发送方必须处理的第三个主要事件是,到达一个来自接收方的确认报文段(ACK)。

1. 一些有趣的情况

图3-34描述了第一种情况:

主机A向主机B发送一个报文段。
假设报文段序号是92,且包含8字节数据。
发出该报文段后,主机A等待一个来自主机B的确认号为100的报文段。
虽然A的报文段被B收到,但从主机B发往A的确认报文丢失了。
这种情况下,超时事件就会发送,主机A会重传相同的报文段。
主机B收到重传报文段时,通过序号可知道此部分数据已经收到,并直接丢弃。

图3-35描述了第二种情况:

主机A连续发回两个报文段。
第一个报文段序号为92,含8字节数据;
第二个报文段序号是100,含20字节数据。
假定两个报文段都到达主机B,主机B为每一个报文段分别发送一个确认。
第一个确认报文的确认号是100,第二个确认报文的确认号是120。
假设超时前,这两个报文段中没有一个确认报文到达主机A。
超时事件发生时,主机A重传序号92的第一个报文段,重启定时器。
只要第二个报文段的ACK在新的超时发生前到达,则第二个报文段将不会重传。

在这里插入图片描述
下图 3-36 描述了第三种情况:

主机A与在情况2中一样,发送两个报文段。
第一个报文段的确认报文在网络丢失,但在超时事件发生前主机A收到一个确认号为120的确认报文。
主机A不会重传这两个报文段的任何一个。

在这里插入图片描述
2. 超时间隔加倍

每当超时事件发生时,TCP重传具有最小序号的还未被确认的报文段。
只是每次TCP重传时都会将下一次超时间隔设为先前值的两倍,而不是用从 EstimatedRTT 和 DevRTT 推算出的值。

例:
假设当定时器第一次过期时,与最早的未被确认的报文段相关联的TimeoutInterval是0.75秒。
TCP会重传该报文段,并把过期时间设为1.5s。
如果1.5s后定时器又过期了,则TCP将再次重传该报文段,并把过期时间设置为3.0s。
因此,超时间隔在每次重传后会呈指数型增长。

每当定时器在另两个事件(即收到上层应用数据和收到ACK)中的任意一个启动时,TimeoutInterval 由最近的EstimatedRTT 值与 DevRTT 值推算得到。

这种修改提供了一个形式受限的拥塞控制。

3. 快速重传

发送方通常可在超时事件发生之前通过注意所谓冗余ACK来较好检测到丢包情况。
冗余ACK(duplicate ACK)就是再次确认某个报文段的ACK,而发送方先前已收到对该报文段的确认。

当TCP接收方收到一个具有这样序号的报文段时,即其序号大于下一个所期望的,按序的报文段,它检测到了数据流中的一个间隔。
这个间隔可能是由于在网络中报文段丢失或重新排序造成的。
因为TCP不使用否定确认,所以接收方不能向发送方发回一个显式的否定确认。
相反,它只是对已经接收到的最后一个按序字节数据进行重复确认(即产生一个冗余ACK)即可。

事件TCP接收方动作
具有所期望序号的按序报文段到达.所有在期望序号及以前的数据都已经被确认延迟的ACK,对另一个按序报文段的到达最多等待500ms,如下个按序报文段在这个时间间隔内没到达,则发送一个ACK
具有所期望序号的按序报文段到达。另一个按序报文段等待ACK传输立即发送单个累计ACK,以确认两个按序报文段
比期望序号大的报文段到达。检测出间隔立即发送冗余ACK,指示下一个期待的字节的序号(其为间隔的低端的序号)
能部分或完全填充接收数据间隔的报文段到达倘若该报文段起始于间隔的低端,立即发送ACK

如果TCP发送方接收到对相同数据的3个冗余ACK,它把这当作一种指示,说明跟在这个已被确认过3次的报文段之后的报文段已经丢失。

一旦收到3个冗余ACK,TCP就执行快速重传(fast retransmit),即为在该报文段的定时器过期之前重传丢失的报文段。

对于快速重传的TCP,用下列代码段代替之前ACK收到事件处理

事件:收到ACK,具有ACK字段值 y
	if (y > SendBase) {
	SendBase=y
	if (当前仍无任何应答报文段)
		启动定时器
		}
	else {/*对已经确认的报文段的一个冗余ACK */
			当y收到的冗余ACK数加1
		if (对y==3 收到的冗余ACK数)
			/* TCP 快速重传 */
			重新发送具有序号y的报文段
		}
	break;

4. 是回退N步还是选择重传

TCP是一个GBN协议还是一个SR协议?

TCP发送方仅需维持已发送过但未被确认的字节的最小序号(SendBase)和下一个要发送的字节的序号(NextSeqNum)。

许多TCP实现会将正确接收但失序的报文段缓存起来。
考虑发送方发送一组报文段 1,2,3…,N,并且所有的报文段都按序无差错地到达接收方时会发生的情况。

进一步假设对分组 n<N 的确认报文丢失,但是其余 N-1确认报文在分别超时前到达发送端。

在该例中,GBN不仅重传分组n,还重传后继所有分组n+1,…,N。在另一方面,TCP将重传至多一个报文段,即报文段n 。
此外,如果对报文段n+1的确认报文在报文段n超时之前到达,TCP甚至不会重传报文段 n 。

对TCP提出的一种修改意见是选择确认(selective acknowledgment),它允许TCP接收方有选择地确认失序报文段,而不是累计确认最后一个正确接收的有序报文段。
当该机制与选择重传结合使用时(即跳过重传那些已被接收方选择性地确认过的报文段),TCP看起来就很像我们通常的 SR 协议。

因此,TCP的差错恢复是GBN和SR的混合体。

流量控制

一条TCP连接的每一侧主机都为该连接设置了接收缓存。
当该TCP连接收到正确,按序的字节后,它就将数据放入接收缓存。

相关联的应用进程会从该缓存中读取数据。如应用程序读取数据慢,数据到达快,会容易使接收缓存溢出。

TCP为它的应用提供流量控制服务(flow-control service)以消除发送方使接收方缓存溢出的可能性。

流量控制因此是一个速度匹配服务,即发送方的发送速率和接收方应用程序的读取速率匹配。
TCP发送方也可能因为IP网络的拥塞而被遏制,这种称为拥塞控制(congestion control)。

TCP通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制。
接收窗口用于给发送方一个指示-该接收方还有多少可用的缓存空间。

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

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

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

L a s t B y t e R c v d − L a s t B y t e R e a d < = R c v B u f f e r LastByteRcvd-LastByteRead<=RcvBuffer LastByteRcvdLastByteRead<=RcvBuffer
接收窗口用rwnd表示,

r w n d = R c v B u f f e r − [ L a s t B y t e R c v d − L a s t B y t e R e a d ] rwnd=RcvBuffer - [LastByteRcvd-LastByteRead] rwnd=RcvBuffer[LastByteRcvdLastByteRead]

主机B通过把当前的rwnd值放入它发给主机A的报文段接收窗口字段中,通知主机A它在该连接的缓存中还有多少可用空间。
开始时,主机B设定rwnd=RcvBuffer。

主机A轮流跟踪两个变量,LastByteSent和LastByteAcked。
这两个变量之间的差 LastByteSent - LastByteAcked ,就是主机A发送到连接中但未被确认的数据量。

通过将未确认的数据量控制在值rwnd以内,可保证主机A不会使主机B的接收缓存溢出。
因此,主机A在该连接的整个生命周期须保证:
L a s t B y t e S e n t − L a s t B y t e A c k e d < = r w n d LastByteSent - LastByteAcked <= rwnd LastByteSentLastByteAcked<=rwnd

假设主机B的接收缓存已经存满,使得rwnd=0。
在将rwnd=0告知主机A后,要假定主机B没任何数据要发给主机A。
由于TCP仅当在它有数据或有确认要发时,才会发送报文段给A。这样此后主机B的rwnd变大时,A收不到通知。

为解决此问题,TCP规范中要求:
主机B的接收窗口为0时,主机A继续发送只有一个字节数据的报文段。
这些报文段将会被接收方确认。最终缓存将开始清空,并且确认报文例将包含一个非0的rwnd值。

TCP连接管理

客户进程先通知客户TCP,想建立一个与服务器上某个进程之间的连接。

客户中的TCP按以下与服务器中的TCP建立连接:

  • 第一步:客户端的TCP先向服务器端的TCP发送一个特殊的TCP报文段。

      报文段中不包含应用层数据
      报文段首部中的一个标志位【SYN】被置为1
      这个特殊报文段称为SYN报文段
      客户会随机地选择一个初始序号,将此编号放置于该起始的TCP SYN报文段的序号字段中。
      该报文段会被封装在一个IP数据报中,发送给服务器。
    
  • 第二步:一旦包含TCP SYN报文段的IP数据报到达服务器主机
    服务器会从该数据报中提取出TCP SYN报文段,为该TCP连接分配TCP缓存和变量,并向该客户TCP发送允许连接的报文段。

      这个允许连接的报文段也不含应用层数据,
      但在报文段首部包含了3 个重要信息。
      SYN比特被置为1
      该TCP报文段首部的确认号字段被置为client_isn+1
      服务器选择自己的初始序号(server_isn),将其放置到TCP报文段首部的序号字段中
      该允许连接的报文段称为SYNACK报文段
    
  • 第三步:收到SYNACK报文段后,客户也要给该连接分配缓存和变量。

      客户主机向服务器发送另外一个报文段,
      这最后一个报文段对服务器的允许连接的报文进行了确认
      因为连接已经建立,所以该SYN比特被置为0
      此报文可在报文段负载中携带客户到服务器的数据。
    

一旦完成上述3步,客户和服务器主机就可相互发包括数据的报文段了。
以后的每一个报文段中,SYN比特将被置为0

在这里插入图片描述参与一条TCP连接的两个进程中的任何一个都能终止该连接。
连接结束后,主机中的"资源"(即缓存和变量)将被释放。
例:
某客户打算关闭连接,客户应用进程发出一个关闭连接命令。
这引起客户TCP向服务器进程发送一个特殊的TCP报文段。
这个特殊的报文段让其首部中的一个标志位即FIN比特被设置为1。
当服务器接收到该报文段后,就向发送方回送一个确认报文段。
此时,两台主机上用于该连接的所有资源都被释放了。

在这里插入图片描述
在一个TCP连接的生命周期内,运行在每台主机中的TCP协议在各种TCP状态(TCP state)之间变迁。

下图说明了客户TCP会经历的一系列典型TCP状态。
客户TCP开始时处于CLOSED状态,客户应用程序发起一个新的TCP连接。
这引起客户中的TCP向服务器中的TCP发送一个SYN报文段。
在发送过SYN报文段后,客户TCP进入SYN_SENT状态。
客户TCP处在SYN_SENT状态时,它等待来自服务器TCP的对客户所发报文段进行确认且SYN比特被置为1的一个报文段。
收到这样一个报文段后,客户TCP进入ESTABLISHED状态。
当处在ESTABLISHED状态时,TCP客户就能发送和接收包含有效载荷数据的TCP报文段了。

在这里插入图片描述
假设客户应用程序决定关闭该连接。(服务器也能选择关闭该连接)
这引起客户TCP发送一个带有FIN比特被置为1的TCP报文段,并进入FIN_WAIT_1状态。
当处于FIN_WAIT_1状态时,客户TCP等待一个来自服务器的带有确认的TCP报文段。
当它收到该报文段时,客户TCP进入FIN_WAIT_2状态。
当处在FIN_WAIT_2状态时,客户等待来自服务器的FIN比特被置为1的另一个报文段;当收到该报文段后,客户TCP对服务器的报文段进行确认,并进入TIME_WAIT状态。
假定ACK丢失,TIME_WAIT状态使TCP客户重传最后的确认报文。
在TIME_WAIT状态中所消耗的时间是与具体实现有关的,典型值是30秒、1分钟或2分钟。
经过等待后,连接正式关闭,客户段所有资源(包括端口号)被释放。

在这里插入图片描述考虑一台主机接收到一个TCP报文段,其端口号或源IP地址与该主机上进行中的套接字都不匹配的情况。

假如一台主机接受了有目的端口80的一个TCP SYN分组,但该主机在端口80不接受连接(即它不在端口80上运行Web服务器)。
则该主机将向源发送一个特殊重置报文段。
该TCP报文段将RST标志位置为1。
它告诉源"我没有那个报文段的套接字,请不要再发送该报文段了"。
当一台主机接收一个UDP分组,它的目的端口与进行中的UDP套接字不匹配,该主机发送一个特殊的ICMP数据报。

回顾nmap端口扫描工具。
为探索目标主机上一个特定的TCP端口,如端口6789,nmap将对那台主机的目的端口6789发送一个特殊的TCP SYN报文段。

有3种可能输出:

  • 源主机从目标主机接收到一个 TCP SYNACK报文段。

      意味着目标主机上一个应用程序使用TCP端口6789运行,nmap返回“打开”。
    
  • 源主机从目标主机接收到一个TCP RST报文段。

      意味着该SYN报文段到达了目标主机,但目标主机没有运行一个使用TCP端口6789的应用程序。
      但至少知道,发送的报文段未被防火墙阻挡。
    
  • 源什么也没有收到。

      可能表明,该SYN报文段被防火墙阻挡
    

拥塞控制原理

拥塞原因与代价

随主机增加其发送速率并使网络变得拥塞,这时会发生的情况。

情况1:两个发送方和一台具有无穷大缓存的路由器

考虑最简单的拥塞情况:
两台主机(A和B)都有一条连接,且这两条连接共享源与目的地之间的单跳路由。

在这里插入图片描述
假设主机A中的应用程序以 λ i n \lambda_{in} λin 字节/秒的平均速率将数据发送到连接中(如,通过一个套接字将数据传递给运输层协议)。
这些数据是初始数据,这意味着每个数据单元仅向套接字中发送一次。
下面的运输层协议是一个简单的协议。
数据被封装并发送;不执行差错恢复(如重传),流量控制,拥塞控制。
忽略由于添加运输层和较低层首部信息产生的额外开销,
第一种情况下,主机A向路由器提供流量的速率是 λ i n \lambda_{in} λin 字节/秒。
主机B也以同样方式运行,为简化问题,假设它也以速率 λ i n \lambda_{in} λin 字节/秒发送数据。
来自主机A和主机B的分组通过一台路由器,在一段容量为R的共享式输出链路上传输。
该路由器带缓存,可用于当分组到达速率超过输出链路容量时存储"入分组"。
此第一种情况下,假设路由器有无限大的缓存空间。

下图描绘了第一种情况下主机A的连接性能。
左边是每连接吞吐量(接收方每秒接收的字节数),与该连接发送速率之间的函数关系。

右边图形显示了以接近链路容量的速率运行时产生的后果。当发送速率接近R/2时(从左至右),平均时延会越来越大。
当发送速率超过R/2时,路由器中的平均排队分组数会无限增长,源与目的地之间的平均时延也会变成无穷大(假设这些连接以此发送速率运行无限长时间且拥有无限量缓存可用)。

在这里插入图片描述
情况2:两个发送方和一台具有有限缓存的路由器

从下列两个方面对情况1稍微做一些修改:

  • 首先,假定路由器缓存的容量有限。
    当分组到达一个已满的缓存时会被丢弃。
  • 其次,假定每条连接是可靠的。
    如果一个包含有运算层报文段的分组在路由器中被丢弃,那么它终将被发送方重传。

分组可被重传。

  • 再次以 λ i n \lambda_{in} λin 字节/秒表示应用程序将初始数据发送到套接字中的速率。
    运输层向网络中发送报文段(含初始数据或重传数据)的速率用 λ i n ′ \lambda'_{in} λin 字节/秒表示。
    λ i n ′ \lambda'_{in} λin有时被称为网络的供给载荷(offersd load)。

在这里插入图片描述情况2下实现的性能依赖于重传的方式。
首先,考虑一种不真实的情况,即主机A能以某种方式确定路由器中的缓存是否空闲,因而仅当缓存空闲时才发送一个分组。
在这种情况下,将不会产生丢包, λ i n \lambda_{in} λin λ i n ′ \lambda'_{in} λin相等,并且连接的吞吐量等于 λ i n \lambda_{in} λin

此时,平均主机发送速率不超过R/2,因为假定不会发生分组丢失。

考虑更真实情况,发送方仅当在确定了一个分组已丢失时才重传。[超时时间足够大]
这时性能可能与下图b所示的情况相似。

考虑供给载荷 λ i n ′ \lambda'_{in} λin等于R/2的情况。
根据图b,在这一供给载荷值时,数据被交付给接收方应用程序的速率是R/3。
因此,在所发送的0.5R单位数据中,从平均角度说,0.333R字节/秒是初始数据,而0.166R字节/秒是重传数据。

拥塞的代价:发送方必须执行重传以补偿因为缓存溢出而丢失的分组

最后考虑下面一种情况:
发送方也许会提前发生超时并重传在队列中已被推迟但还未丢失的分组。
这时,初始数据分组和重传分组都可能到达接收方。
接收方只接收一份分组副本,重传分组将被丢弃。

这里又看到了网络拥塞的另一种代价。
拥塞的代价:发送方在遇到大时延时所进行的不必要重传会引起路由器利用其链路带宽来转发不必要的分组副本。

下图c显示了假定每个分组被路由器平均转发两次时,吞吐量与供给载荷的对比情况。
供给载荷解决R/2时,吞吐量渐进R/4。

在这里插入图片描述
情况3:4个发送方和具有有限缓存的多台路由器及多跳路径

在最后一种拥塞情况中,有4台主机发送分组,每台通过交叠的两跳路径传输。

假设每台主机采用超时/重传机制来实现可靠数据传输,
所有主机有相同的 λ i n \lambda_{in} λin值,所有路由器的链路容量都是R字节/秒。

考虑主机A到主机C的连接,该连接经过路由器R1和R2.
A - C连接与D - B连接共享路由器R1,并与B - D连接共享路由器R2。
对极小的 λ i n \lambda_{in} λin值,路由器缓存的溢出是很少见的,吞吐量大致接近供给载荷。

对稍大的 λ i n \lambda_{in} λin值,对应的吞吐量也更大,因为有更多的初始数据发到网络中并交付到目的地,溢出仍然很少。
因此,对于较小的 λ i n \lambda_{in} λin λ i n \lambda_{in} λin的增大会导致 λ o u t \lambda_{out} λout的增大。

在这里插入图片描述下面分析当 λ i n \lambda_{in} λin很大时的情况:

不管 λ i n \lambda_{in} λin 的值是多大,到达路由器R2的A-C流量(经过路由器R1转发后达到路由器R2)的到达速率至多是R,也就是从R1到R2的链路容量。

λ i n ′ \lambda'_{in} λin对所有链路(包括B-D连接)来说是极大的值,那么在R2上,B-D流量的到达速率可能比A-C流量的到达速率大的多。
因为A-C流量与B-D流量在路由器R2上必须为有限缓存空间竞争,所以,当来自B-D连接的供给载荷越來越大时,A-C连接上成功通过R2的流量会越来越小。

在极限情况下,当供给载荷趋近于无穷大时,R2的空闲缓存会立即被B-D连接的分组占满,因而A-C连接在R2上的吞吐量趋近于0。
这又一次说明,在重载的极限情况下,A-C端到端吞吐量将趋近于0。
这引发了供给载荷与吞吐量之间的权衡。

在这里插入图片描述
当考虑由网络所做的浪费掉的工作量时,随着供给载荷的增加而使吞吐量最终减少的原因是明显的。

在上面的大流量情况,每当有一个分组在第二跳路由器上被丢弃时,第一跳路由器所做的将分组转发到第二跳路由器的工作就是"劳而无功"的。

如果第一跳路由器只是丢弃该分组并保持空闲,则网络中的情况是幸运的(更准确地说是糟糕的)。

第一跳路由器所使用的将分组转发到第二跳路由器的传输容量用来传送不同的分组可能更有效益。(如选择一个分组发送时,选择经过路由器个数多的。)

拥塞的代价:当一个分组沿一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量被浪费掉了。

拥塞控制方法

在最为宽泛级别上,可根据网络层是否为运输层拥塞控制提供了显式帮助,来区分拥塞控制方法。

  • 端到端拥塞控制。
    在端到端拥塞控制方法中,网络层没有为运算层拥塞控制提供显式支持。
    TCP报文段的丢失(超时或3次冗余确认)被认为是拥塞一个迹象。
  • 网络辅助的拥塞控制。
    在网络辅助的拥塞控制中,路由器向发送方提供关于网络中拥塞状态的显式反馈信息。

对网络辅助的拥塞控制,拥塞信息从网络反馈到发送方有两种方式:

在这里插入图片描述
直接反馈信息可由网络路由器发给发送方。

路由器标记或更新从发送方流向接收方的分组中的某个字段来指示拥塞的产生。一旦收到一个标记的分组后,接收方就向发送方通知该网络拥塞指示。

TCP拥塞控制

TCP采用的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率。

这种方法提出了三个问题:

  • TCP发送方如何限制它向其连接发送流量的速率呢?
  • TCP发送方如何感知从它到目的地之间的路径上的拥塞?
  • 发送方感知到端到端拥塞时,采用何种算法来改变其发送速率?

TCP发送方是如何限制向其连接发送流量的。

TCP连接的每一端由一个接收缓存,一个发送缓存,几个变量(LastByteRead、rwnd等)组成。
运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即拥塞窗口(congestion window)。
拥塞窗口表示为 cwnd,它对一个TCP发送方能向网络中发送流量的速率进行限制。
一个发送方中未被确认的数据量不会超过cwnd与rwnd的最小值,即

L a s t B y t e S e n t − L a s t B y t e A c k e d < = m i n c w n d , r w n d LastByteSent - LastByteAcked <= min{cwnd, rwnd} LastByteSentLastByteAcked<=mincwnd,rwnd

为关注拥塞控制,假设TCP接收缓存足够大,可忽略接收窗口限制;因此发送方中未被确认的数据量仅受限于cwnd。
假设发送方总是有数据发送,即在拥塞窗口中的所有报文段要被发送。

考虑一个丢包和发送时延均可忽略不计的连接。
在每个往返时间(RTT)的起始点,上面的限制允许发送方向该连接发送cwnd个字节的数据,在该RTT结束时发送方接收对数据的确认报文。

发送方的发送速率大概是cwnd/RTT字节/秒。
通过调节cwnd,发送方能调整它向连接发送数据的速率

TCP发送方是如何感知在它与目的地之间的路径上出现了拥塞的。

将一个TCP发送方的丢包事件定义为:要么超时,要么收到来自接收方的3个冗余ACK。
当出现过度拥塞时,在沿着这条路径上一台(或多台)路由器的缓存会溢出,引起一个数据报(包含一个TCP报文段)被丢弃。
丢弃的数据报会引起发送方的丢包事件(要么超时或收到3个冗余ACK),发送方认为在发送方到接收方路径上出现了拥塞的指示。

考虑没有丢包。此时,在TCP发送方将收到对于以前未确认报文段的确认。
TCP将这些确认的到达作为一切正常的指示,即在网络上传输的报文段正被成功地交付给目的地,并使用确认来增加窗口长度(及其传输速率)。

因为TCP使用确认来触发(或计时)增大它的拥塞窗口长度,TCP被说成是自计时(self-clocking)的。

给定调节cwnd值以控制发送速率的机制, TCP发送方怎样确定它应当发送的速率呢?

拥塞窗口调节以便既使得网络不会拥塞,又能充分利用可用的带宽。

窗口调节指导性原则:

  • 一个丢失的报文段意味着拥塞,当丢失报文段时应降低TCP发送方的速率。
  • 一个确认报文段指示该网络正在向接收方交付发送方的报文段,因此,对先前未确认报文段的确认到达时,能增加发送方的速率。
  • 带宽探测。
    给定ACK指示源到目的地无拥塞,而丢包事件指示路径拥塞,TCP调节其传输速率的策略是增加其速率以响应到达的ACK,除非出现丢包事件,才减小传输速率。

为探测拥塞开始出现的速率,TCP发送方增加它的传输速率,从该速率后退,再次探测。

ACK和丢包事件充当了隐式信号,并且每个TCP发送方根据异步于其他TCP发送方的本地信息而行动。

TCP拥塞控制算法(TCP congestion control algorithm),包含三个主要部分:

  • 慢启动
  • 拥塞避免
  • 快速恢复【可选】

1. 慢启动

一条TCP连接开始时,cwnd的值常初始置为一个MSS的较小值,使初始发送速率大约为MSS/RTT。

由于对TCP发送方而言,可用带宽可能比MSS/RTT大得多,TCP发送方希望迅速找到可用带宽数量。

慢启动状态,cwnd以1个MSS开始且每当传输的报文段首次被确认就增加1个MSS。
TCP发送速率起始慢,但以指数增长。

在这里插入图片描述
何时结束指数增长?

  • 首先,如果存在一个由超时指示的丢包事件(即拥塞),TCP发送方将cwnd设置为1并重新开始慢启动过程。
    它还将第二个状态变量值ssthresh设为cwnd/2,即当检测到拥塞时将ssthresh置为拥塞窗口值的一半。

  • 慢启动结束的第二种方式直接与ssthresh值关联。
    当检测到拥塞时ssthresh设为cwnd的值的一半,当到达或超过ssthresh,继续使 cwnd 翻番可能有些鲁莽。
    因此,当cwnd等于ssthresh时,结束慢启动且TCP转移到拥塞避免模式。
    进入拥塞避免模式时,TCP更谨慎地增加cwnd。

  • 最后一种结束慢启动的方式是,
    如检测到3个冗余ACK,这时TCP执行一种快速重传并进入快速恢复状态。

在这里插入图片描述

2. 拥塞避免

一旦进入拥塞避免状态,cwnd值大约是上次遇到拥塞时值的一半,即距离拥塞可能并不遥远!

TCP无法每过一个RTT再将cwnd的值翻番,而是采用一较为保守方法,每个RTT只将cwnd值增加一个MSS。

一种通用的方法是对于TCP发送方无论何时到达一个新的确认,就将cwnd增加一个MSS*(MSS/cwnd)字节。

例如,如果MSS是1460字节,并且cwnd是14600字节,则在一个RTT内发送10个报文段。
每个到达ACK(假定每个报文段一个ACK)增加1/10 MSS的拥塞窗口长度,因此在收到对所有10个报文段的确认后,拥塞窗口的值将增加一个MSS。

何时应结束拥塞避免的线性增长?
出现超时时,TCP的拥塞避免算法行为相同。
cwnd被设置为1个MSS,当丢包事件出现时,ssthresh值被更新为cwnd值一半。

由三个冗余ACK触发的丢包,网络继续从发送方向接收方交付报文段。
TCP对这种丢包事件的行为,相比于超时指示的丢包,应当不那么剧烈:TCP将cwnd值减半,且收到3个冗余ACK,将ssthresh值记录为cwnd的值的一半,进入快速恢复状态。

3. 快速恢复

对引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK,cwnd的值增加一个MSS。

当对丢失报文段的一个ACK到达时,TCP在降低cwnd后进入拥塞避免状态。
如出现超时事件,快速恢复在执行如同在慢启动和拥塞避免中相同的动作后,迁移到慢启动状态:
当丢包事件出现时,cwnd被设置为一个MSS,且ssthresh设置为cwnd值的一半。

快速恢复是TCP推荐而非必须构件。
在TCP早期版本Tahoe,不管是超时指示的丢包事件,还是发生3个冗余ACK指示的丢包事件,都无条件地将其拥塞窗口减至1个MSS,并进入慢启动阶段。

TCP较新版本TCP Reno,则综合了快速恢复。
在这里插入图片描述
4. TCP拥塞控制:回顾

忽略一条连接开始时初始的慢启动阶段,假定丢包由3个冗余的ACK而不是超时指示,TCP的拥塞控制是:
每个RTT内cwnd线性(加性)增加1MSS,然后出现3个冗余ACK事件时cwnd减半(乘性减)。
因此,TCP拥塞控制常常被称为加性增乘性减拥塞控制方式。

在这里插入图片描述许多Tcp实现采用了Reno算法
Tcp Vegas基本思想:

  • 分组丢失发生前,在源与目的地之间检测路由器的拥塞;
  • 当检测出快要发生分组丢失时,线性地降低发送速率。
    快要发生分组丢失通过观察RTT来预测的。

TCP的 Ubuntu Linux 实现默认提供慢启动,拥塞避免,快速恢复,快速重传和SACK,也提供了诸如TCP Vegas 和 BIC [Xu 2004] 等其他拥塞控制算法。

5. 对TCP吞吐量的宏观描述

要考虑一个长存活期的TCP连接的平均吞吐量。

在一个特定的往返间隔内,TCP发送数据的速率是拥塞窗口与当前RTT的函数。
当窗口长度是w字节,且当前往返时间是RTT秒时,则TCP的发送速率大约是w/RTT。

TCP通过每经过一个RTT将w增加一个MSS探测出额外的带宽,直到一个丢包事件发生为止。
当一个丢包事件发生时,用W表示w的值。

假设在连续持续期间RTT和W几乎不变,则TCP的传输速率在W/(2*RTT)到W/RTT之间变化。
这导出了TCP稳态行为的一个高度简化的宏观模型。
当速率增长至W/RTT时,网络丢弃来自连接的分组;
然后发送速率减半,进而每过一个RTT就发送速率增加MSS/RTT,直到再次达到W/RTT。

这一过程不断自我重复。因为TCP吞吐量(即速率)在两个极值之间线性增长,故有

一 条 连 接 的 平 均 吞 吐 量   = ( 0.75 ∗ W ) / R T T 一条连接的平均吞吐量 = (0.75*W)/RTT  =(0.75W)/RTT

6. 经高带宽路径的TCP

TCP继续演化的需求能够通过考虑网格和云计算应用所需要的高速TCP连接加以阐述。

考虑有1500字节报文段和100ms RTT的TCP连接,假定要通过这条连接以10Gbps速率发送数据。
注意到使用上述吞吐量公式,为取得10Gbps吞吐量,平均拥塞窗口长度将需要是83333个报文段。

对如此大量的报文段,在丢失下,或者说,这些传输的报文段能以何种比例丢失,使得在上述TCP拥塞控制算法仍能取得10Gbps速率:

一 条 连 接 的 平 均 吞 吐 量   =   ( 1.22 ∗ M S S ) / ( R T T ∗ s q r t ( L ) ) 一条连接的平均吞吐量 = (1.22*MSS)/(RTT*sqrt(L))   (1.22MSS)/(RTTsqrt(L))

公平性

考虑K条TCP连接,每条有不同的端到端路径,但都经过一段传输速率为Rbps的瓶颈链路。

假设每条连接都在传输一个大文件且无UDP流量通过该段瓶颈链路。
如果每条连接的平均传输速率接近R/K,则认为该拥塞控制机制是公平的。

TCP的AIMD公平吗?
尤其在假定可在不同时间启动,在给定时间可能具有不同窗口长度下,
TCP趋于在竞争的多条TCP连接间提供对一段瓶颈链路带宽的平等分享。

考虑有两条TCP连接共享一段传输速率为R的链路的例子。
假设两条连接有相同的MSS和RTT,它们有大量的数据要发送,且没有其他TCP连接或UDP数据报穿越该段共享链路。
忽略TCP的慢启动阶段,假设TCP连接一直按CA模式(AIMD)运行。

在这里插入图片描述

如TCP要在这两条TCP连接间平等地共享链路带宽,则实现的吞吐量曲线应该是从原点沿45度方向的箭头向外辐射。
理想情况是,两个吞吐量的和等于R。
目标是使取得的吞吐量落在上图中平等带宽共享曲线与全带宽利用曲线的交叉点附近的某处。

假定TCP窗口长度,在某给定时刻,连接1和连接2实现了上图A点所指明的吞吐量。
因为两条连接共同消耗的链路带宽量小于R,所以无丢包事件发生,根据TCP的拥塞避免算法结果,这两条连接每过一个RTT都要将其窗口增加一个MSS。

因此,两条连接的总吞吐量会从A点开始沿45度线前行。
最终,这两条连接共同消耗的带宽将超过R,最终发生分组丢失。

假设连接1,连接2实现B点指明吞吐量时,它们都经历了分组丢失。
连接1和连接2按二分之一减小其窗口。
所产生的结果实现了C点指明的吞吐量,它正好位于始于B点止于原点的一个向量的中间。
之后再次线性增加,如此循环往复。

1. 公平性和UDP

UDP是没有内置的拥塞控制的。

从TCP的观点来看,运行在UDP上的多媒体应用是不公平的,因为它们不与其他连接合作,也不适时地调整其传输速率。

UDP源有可能压制TCP流量。

2. 公平性和并行TCP连接

多条连接的确切数目可以在多数浏览器中进行配置。
当一个应用使用多条并行连接时,它占用了一条拥塞链路中较大比例的带宽。

明确拥塞通告:网络辅助拥塞控制

TCP发送方不会收到网络层关于拥塞的指示,而是通告观察分组丢失来判断拥塞。
对IP和TCP的扩展方案允许网络向TCP发送方和接收方发出拥塞信号。这种形式的网络辅助拥塞控制称为明确拥塞通告(Explicit Congestion Notification, ECN)。

在这里插入图片描述在网络层,IP数据报首部的服务类型字段中的两个比特被用于ECN。
路由器所使用的一种ECN比特设置指示该路由器正在历经拥塞。
拥塞指示由被标记的IP数据报所携带,送给目的主机,再由目的主机通知发送主机。

RFC 3168推荐仅当拥塞持续不断存在时才设置ECN比特。
发送主机所使用的另一种ECN比特设置通知路由器发送方和接收方是ECN使能的,因此能够对于ECN指示的网络拥塞采取行动。

当接收主机的TCP通过一个收到的ECN拥塞指示时,接收主机中的TCP通过在接收方到发送方的TCP ACK报文段中设置 ECE(明确拥塞通告回显) 比特,通知发送主机中的TCP收到拥塞指示。

TCP发送方通过减半拥塞窗口对一个具有ECE拥塞指示的ACK做反应,就像它对丢失报文段使用快速重传做出反应一样,并且在下一个传输的TCP发送方到接收方报文段首部中对CWR(拥塞窗口缩减)比特进行设置。

学习参考资料:

《计算机网络——自顶向下方法》 第7版
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值