计算机网络之运输层

运输层

运输层协议概述

进程之间的通信

从通信和信息处理的角度上来看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。当网络的边缘部分中的两台主机使用网络的核心部分的功能进行端到端的通信时,只有主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。

下面通过下面的示意图来说明运输层的作用。设局域网LAN1上的主机A和局域网LAN2上的主机B通过互连的广域网WAN进行通信。我们知道,IP协议能够把源主机A发送出的分组,按照IP首部中的目的地址,送交到目的主机B。那么,这样做似乎就已经可以实现两台主机之间的通信了,为什么还需要运输层呢?
请添加图片描述
从IP层来说,通信的两端是两台主机。IP数据报的首部明确地标志了这两台主机的IP地址。但“两台主机之间的通信”这种说法还不够清楚。这是因为,真正进行通信的实体是在主机中的进程,是这台主机中的一个进程和另一台主机中的一个进程在交换数据(即通信)。因此严格地讲,两台主机进行通信就是两台主机中的应用进程互相通信。IP协议虽然能把分组送到目的主机,但是这个分组还停留在主机的网络层而没有交付主机中的应用进程。从运输层的角度看,通信的真正端点并不是主机而是主机中的进程。也就是说,端到端的通信是应用进程之间的通信。

在一台主机中经常有多个应用进程同时分别和另一台主机中的多个应用进程通信。例如,某用户在使用浏览器查找某网站的信息时,其主机的应用层运行浏览器客户进程。如果在浏览网页的同时,还要用电子邮件给网站发送反馈意见,那么主机的应用层就还要运行电子邮件的客户进程。

在上图中,主机A的应用进程AP1和主机B的应用进程AP2通信,而与此同时,应用进程AP2也和对方的应用进程AP4通信。这表明运输层有一个很重要的功能——复用(multiplexing)和分用(demultiplexing)。

这里的“复用”是指在发送方不同的应用进程都可以使用同一个运输层协议传送数据,而“分用”是指接收方的运输层在剥去报文的首部后能够把这些数据正确交付目的应用进程。

运输层要对收到的报文进行差错检测。在之前的网络层,IP数据报首部中的检验和字段只检验首部是否出现差错而不检查数据部分。而运输层的差错检验,UDP和TCP的检验都需要检查数据部分。

根据应用程序的不同需求,运输层需要有两种不同的运输协议,即面向连接的TCP和无连接的UDP。

运输层的两个主要协议

TCP/IP运输层的两个主要协议都是互联网的正式标准,即:

  1. 用户数据报协议UDP(User Datagram Protocol) [RFC 768]
  2. 传输控制协议TCP(Transmission Control Protocol) [RFC 793]

它们在协议栈中的位置如下:
请添加图片描述
按照OSI的术语,两个对等运输实体在通信时传送的数据单位叫做运输协议数据单元TPDU(Transport Protocol Data Unit)。但在TCPP体系中,根据所使用的协议是TCP还是UDP,分别称之为TCP报文段(segment)或UDP用户数据报

UDP在传送数据之前不需要先建立连接。远地主机的运输层在收到UDP报文后,不需要给出任何确认。虽然UDP不提供可靠交付,但在某些情况下UDP却是一种最有效的工作方式。

TCP则提供面向连接的服务。在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播或多播服务。由于TCP要提供可靠的、面向连接的运输服务,因此不可避免地增加了许多的开销,如确认、流量控制、计时器以及连接管理等。这不仅使协议数据单元的首部增大很多,还要占用许多的处理机资源。

下表给出了一些应用和应用层协议所使用的运输层协议:
请添加图片描述

运输层的端口

应用层所有的应用进程都可以通过运输层再传送到IP层(网络层),这就是复用。运输层从IP层收到发送给各应用进程的数据后,必须分别交付指明的各应用进程,这就是分用。显然,给应用层的每个应用进程赋予一个非常明确的标志是至关重要的。

我们知道,在单个计算机中的进程是用进程标识符(一个不大的整数)来标志的。但是在互联网环境下,用计算机操作系统所指派的这种进程标识符来标志运行在应用层的各种应用进程则是不行的。这是因为在互联网上使用的计算机的操作系统种类很多,而不同的操作系统又使用不同格式的进程标识符。

为了使运行不同操作系统的计算机的应用进程能够互相通信,就必须用统一的方法(而这种方法必须与特定操作系统无关)对TCP/IP体系的应用进程进行标志。

解决这个问题的方法就是在运输层使用协议端口号(protocol port number),或通常简称为端口(port)。这就是说,虽然通信的终点是应用进程,但只要把所传送的报文交到目的主机的某个合适的目的端口,剩下的工作(即最后交付目的进程)就由TCP或UDP来完成。

请注意,这种在协议栈层间的抽象的协议端口是软件端口,和路由器或交换机上的硬件端口是完全不同的概念。硬件端口是不同硬件设备进行交互的接口,而软件端囗是应用层的各种协议进程与运输实体进行层间交互的一种地址。对于协议端口,不同的系统具体实现端口的方法可以是不同的。

TCP/IP的运输层用一个16位端口号来标志一个端口。但请注意,端口号只具有本地意义,它只是为了标志本计算机应用层中的各个进程在和运输层交互时的层间接口。在互联网不同计算机中,相同的端口号是没有关联的。16位的端口号可允许有65535个不同的端口号。

运输层的端口号分为以下两大类:

  1. 服务器端使用的端口号。这里又分为两类,一类是熟知端口号(well-known port number),数值为0~1023,IANA(www.iana.org)把这些端口号指派给了TCP/IP最重要的一些应用程序。当一种新的应用程序出现后,IANA必须给它指派一个熟知端口,否则互联网上的其他应用进程就无法和它进行通信。

下表列出了一些常用的熟知端口号:
请添加图片描述
另一类叫登记端口号,数值为1024~49151。这类端口号是为没有熟知端口号的应用程序使用的。使用这类端口号必须在IANA按照规定的手续登记,以免重复登记。

  1. 客户端使用的端口号。由于这类端口号仅在客户进程运行时才动态选择,因此又叫做短暂端口号。这类端口号留给客户进程选择暂时使用。当服务器进程收到客户进程的报文时,就知道了客户进程所使用的端口号,因而可以把数据发送给客户进程。通信结束后,刚才已使用过的客户端口号就不复存在,这个端口号就又可以供其他客户进程使用。

用户数据报协议UDP

UDP概述

用户数据报协议UDP只在IP的数据报服务之上增加了很少一点的功能,这就是复用和分用以及差错检测的功能。UDP的主要特点是:

(1) UDP是无连接的,即发送数据之前不需要建立连接(当然,发送数据结束时也没有连接可释放),因此减少了开销和发送数据之前的时延。

(2) UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表(这里面有许多参数)。

(3) UDP是面向报文的。发送方的UDP对应用程序交付下来的报文,在添加首部后就直接向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。这就是说,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文,如下图所示:
请添加图片描述
而接收方的UDP,对IP层交上来的UDP用户数据报,在去除首部后就原封不动地交付上层的应用进程。也就是说,UDP一次交付一个完整的报文。

因此,应用程序必须选择合适大小的报文。若报文太长,UDP把它交给IP层后,IP层在传送时可能要进行分片,这会降低IP层的效率。反之,若报文太短,UDP把它交给IP层后,会使IP数据报的首部的相对长度太大,这也降低了IP层的效率。
(4) UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低,这对某些实时应用是很重要的。很多的实时应用(如IP电话、实时视频会议等)要求源主机以恒定的速率发送数据,并且允许在网络发生拥塞时丢失一些数据,但却不允许数据有太大的时延。UDP正好适合这种要求。
(5) UDP支持一对一、一对多、多对一和多对多的交互通信
(6) UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。

UDP的首部格式

用户数据报UDP有两个字段:数据字段和首部字段。首部字段很简单,只有8个字节,有四个字段组成,每个字段的长度都是两个字节。各字节意义如下:

(1) 源端口 源端口号。在需要对方回信时选用。不需要时可用全0.

(2) 目的端口 目的端口号。这在终点交付时必须使用。

(3) 长度 UDP用户数据报的长度,其最小值是8(仅有首部,数据部分为空)。

(4) 检验和 检验UDP用户数据报在传输中是否有错。有错就丢弃。

请添加图片描述
当运输层从IP层收到UDP数据报时,就根据首部中的目的端口,把UDP数据报通过相应的端口,上交最后的终点——应用进程。下图是UDP基于端口分用的示意图:
请添加图片描述
如果接收方UDP发现收到的报文中的目的端口号不正确(即不存在对应于该端口号的应用进程),就丢弃该报文,并由网际控制报文协议ICMP发送“端口不可达”差错报文给送方(traceroute命令的使用原理)。

注:虽然在UDP之间的通信要用到端口号,但由于UDP的通信是无连接的,因此不需要使用套接字( 这点与TCP不同,TCP之间的通信必须要在两个套接字之间建立连接)。

UDP 数据报的校验

UDP用户数据报首部中检验和的计算方法有些特殊。在计算检验和时,要在UDP用户数据报之前增加12个字节的伪首部。所谓“伪首部”是因为这种伪首部并不是UDP用户数据报真正的首部。只是在计算检验和时,临时添加在UDP用户数据报前面,以得到一个临时的UDP用户数据报。检验和就是按照这个临时的UDP用户数据报来计算的。

伪首部既不向下传送也不向上递交,而仅仅是为了计算检验和。UDP计算检验和的方法和计算IP数据报首部检验和的方法相似,但不同的是,IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分一起都检验。

在发送方,首先是先把全零放入检验和字段。再把伪首部以及UDP用户数据报看成是由许多16位的字串接起来的。若UDP用户数据报的数据部分不是偶数个字节,则要填入一个全零字节(但此字节不发送)。然后按二进制反码计算出这些16位字的和。将此和的二进制反码写入检验和字段后,就发送这样的UDP用户数据报。

在接收方,把收到的UDP用户数据报连同伪首部(以及可能的填充全零字节)一起,按二进制反码求这些16位字的和。当无差错时其结果应为全1。否则就表明有差错出现,接收方就应丢弃这个UDP用户数据报(也可以上交给应用层,但附上出现了差错的警告)。

下图给出了一个计算UDP检验和的例子。这里假定用户数据报的长度是15字节,因此要添加一个全0的字节。
请添加图片描述
不难看出,这种简单的差错检验方法的检错能力并不强,但它的好处是简单,处理起来较快。

传输控制协议TCP

TCP最主要的特点

(1) TCP是面向连接的运输层协议。这就是说,应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。

(2) 每一条TCP连接只能有两个端点(endpoint),每一条TCP连接只能是点对点的(一对一)。

(3) TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复,并且按序到达。

(4) TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。

在发送时应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。

(5) 面向字节流。TCP中的“流”( stream)指的是流入到进程或从进程流出的字节序列面向字节流”的含义是:

虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。TCP并不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系(例如,发送方应用程序交给发送方的TCP共10个数据块,但接收方的TCP可能只用了4个数据块就把收到的字节流交付上层的应用程序)。但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。当然,接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据。

请添加图片描述
在实际的网络中,一个TCP报文段包含上千个字节是很常见的,而上图都只画了几个字节,这当然是为了更方便地说明“面向字节流”的概念。另一点很重要的是:上图中的TCP连接是一条虚连接(也就是逻辑连接),而不是一条真正的物理连接。TCP报文段先要传送到IP层,加上IP首部后,再传送到数据链路层。再加上数据链路层的首部和尾部后,才离开主机发送到物理链路。

TCP和UDP在发送报文时所采用的方式完全不同。TCP并不关心应用进程一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP发送的报文长度是应用进程给出的)。如果应用进程传送到TCP缓存的数据块太长,TCP就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。

TCP的连接端点——Socket

TCP把连接作为最基本的抽象。前面讲过,每一条TCP连接都有两个端点,TCP连接的端点叫套接字(Socket)。根据RFC 793的定义:端口号拼接到IP地址即构成了套接字。

套接字 Socket = (IP地址端口号)

每一条TCP连接唯一地被通信两端(即两个套接字)所确定,即:

TCP连接 : :  = {socket1socket2} = {(IP1:port1), (IP2:port2)}

注意:同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以在出现在多个不同的TCP连接中。

可靠传输的工作原理

我们知道,TCP发送的报文段是交给IP层传送的。但IP层只能提供尽最大努力服务。也就是说,TCP下面的网络所提供的是不可靠的传输。因此,TCP必须采用适当的措施才能使得两个运输层之间的通信变得可靠。

理想的传输条件有以下两个特点:

(1) 传输信道不产生差错;

(2) 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。

然而,实际的网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。这样一来,本来不可靠的传输信道就能够实现可靠传输了。

停止等待协议

我们首先从停止等待协议开始讲起。所谓“停止等待”,意思就是说每发送一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。

无差错情况

停止等待协议可用下图来说明,图(a)是最简单的无差错情况:
请添加图片描述

出现差错

图(b)是分组在传输过程中出现差错的情况。B接收M1时检测出了差错,就丢弃M1,其他什么也不做(不通知A收到有差错的分组)。也可能是M1在传输过程中丢失了,这时B当然什么都不知道。在这两种情况下,B都不会发送任何信息。

可靠传输协议是这样设计的:A只要超过了一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组,这就叫做超时重传。要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。其实在上图(a)中,A为每一个已发送的分组都设置了一个超时计时器。但A只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器。

这里应注意以下三点:

  • 第一,A在发送完一个分组后,必须暂时保留已发送的分组的副本(在发生超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本。
  • 第二,分组和确认分组都必须进行编号。这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认。
  • 第三,超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。如果重传时间设定得很长,那么通信的效率就会很低。但如果重传时间设定得太短,以致产生不必要的重传,就浪费了网络资源。然而,在运输层重传时间的准确设定是非常复杂的,这是因为已发送出的分组到底会经过哪些网络,以及这些网络将会产生多大的时延(这取决于这些网络当时的拥塞情况),这些都是不确定因素。

确认丢失和确认迟到

下图(a)说明的是另一种情况,B所发送的对M1的确认丢失了。A在设定的超时重传时间内没有收到确认,并无法知道是自己发送的分组出错、丢失,或者是B发送的确认丢失了。因此A在超时计时器到期后就要重传M1。现在应注意B的动作,假定B又收到了重传的分组M1。这时应采取两个行动。

  • 第一,丢弃这个重复的分组M1,不向上层交付。
  • 第二,向A发送确认。不能认为已经发送过确认就不再发送,因为A之所以重传M1。
    请添加图片描述
    图(b)也是一种可能出现的情况。传输过程中没有出现差错,但B对分组 M1 的确认迟到了。A会收到重复的确认,对重复的确认的处理很简单:收下后就丢弃。B仍然会收到重复的M1,并且同样要丢弃重复的M1,并重传确认分组。

通常A最终总是可以收到对所有发出的分组的确认。如果A不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信像上述的这种可靠传输协议常称为自动重传请求ARQ(Automatic Repeat reQuest)。意思是重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组。

信道利用率

停止等待协议的优点是简单,但缺点是信道利用率太低。可以用下图展示:
请添加图片描述
信道利用率U可用下式计算:

U = T D T D + R T T + T A U = \frac{T_{D}}{T_{D} + RTT + T_{A}} U=TD+RTT+TATD

连续ARQ协议

滑动窗口协议比较复杂,它是TCP协议的精髓所在。

下图表示发送方维持的发送窗囗,它的意义是:位于发送窗口内的5个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。

在讨论滑动窗口时,我们应当注意到,图中还有一个时间坐标(但以后往往省略这样的时间坐标)。按照习惯,“向前”是指“向着时间增大的方向”,而“向后”则是“向着时间减少的方向”。分组发送是按照分组序号从小到大发送的。
请添加图片描述
连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。上图表示发送方收到了对第1个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。
请添加图片描述

接收方一般都是采用累积确认的方式。这就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示:到这个分组为止的所有分组都已正确收到了。

累积确认有优点也有缺点。优点是:容易实现,即使确认丢失也不必重传。但缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息。

例如,如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫做 Go-back-N(回退N),表示需要再退回来重传已发送过的N个分组。可见当通信线路质量不好时,连续ARQ协议会带来负面的影响。

TCP报文段的首部格式

TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部和数据两部分,而TCP的全部功能都体现在它首部中各字段的作用。因此,只有弄清TCP首部各字段的作用才能掌握TCP的工作原理。

TCP报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此TCP首部的最小长度是20字节。
请添加图片描述
(1) 源端口和目的端口 各占2个字节,分别写入源端口号和目的端口号。和UDP的分用相似,TCP的分用功能也是通过端口实现的。

(2) 序号 占4字节。序号范围是[0,232 − 1],共232(即4294967296)个序号。序号增加到232 − 1后,下一个序号就又回到0。也就是说,序号使用mod 232运算。TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则指的是本报文段所发送的数据的第一个字节的序号。例如,一报文段的序号字段值是301,而携带的数据共有100字节。这就表明:本报文段的数据的第一个字节的序号是301,最后一个字节的序号是400。显然,下一个报文段(如果还有的话)的数据序号应当从401开始,即下一个报文段的序号字段值应为401。这个字段的名称也叫做“报文段序号”。

(3) 确认号 占4字节,是期望收到对方下一个报文段的第一个数据字节的序号。例如,B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501~700),这表明B正确收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701。请注意,现在的确认号不是501,也不是700,而是701。

(4) 数据偏移 占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段实际上是指出TCP报文段的首部长度。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的。但应注意,“数据偏移”的单位是32位字(即以4字节长的字为计算单位)。由于4位二进制数能够表示的最大十进制数字是15,因此数据偏移的最大值是60字节,这也是TCP首部的最大长度(即选项长度不能超过40字节)。

(5) 保留位 占6位,保留为今后使用,但目前应置为0。下面有6个控制位,用来说明本报文段的性质,它们的意义见下面的(6)~(11)。

(6) 紧急 URG(URGent) 当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。

例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行。因此用户从键盘发出中断命令( Control+C)。如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。

当URG置1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。这时要与首部中紧急指针( Urgent Pointer)字段配合使用。

(7) 确认ACK(ACKnowledgment) 仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1。

(8) 推送PSH(PuSH) 当两个应用进程进行交互式的通信时,有时在一端的应用进程,希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。虽然应用程序可以选择推送操作,但推送操作其实很少使用。

(9) 复位RST( ReSeT) 当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃等),必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也可称为重建位或重置位。

(10) 同步SYN( SYNchronization)在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN=1和ACK=1。因此,SYN置为1就表示这是一个连接请求或连接接受报文。关于连接的建立和释放,在后面的59节还要进行详细讨论

(11) 终止FN( FINis) 用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。

(12) 窗口 占2字节。窗口值是[0,216-1]之间的整数。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量(以字节为单位)。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。总之,窗口值作为接收方让发送方设置其发送窗口的依据。

例如,发送了一个报文段,其确认号是701,窗口字段是1000。这就是告诉对方:“从701号算起,我(即发送此报文段的一方)的接收缓存空间还可接收1000个字节数据(字节序号是701~1700),你在给我发送数据时,必须考虑到这一点!”

(13) 检验和 占2字节。检验和字段检验的范围包括首部和数据这两部分。和UDP用户数据报一样,在计算检验和时,要在TCP报文段的前面加上12字节的伪首部,伪首部的格式和前面讲到的UDP的伪首部一样,但应把伪首部第4个字段中的17改为6(TCP的协议号是6),把第5字段中的UDP长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用IPv6,则相应的伪首部也要改变。

(14) 紧急指针 占2字节。紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。因此,紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据。

(15) 选项长度 可变,最长可达40字节。当没有使用“选项“时,TCP的首部长度是20字节。TCP最初只规定了一种选项,即最大报文段长度 MSS(Maximum Segment Size)[RFC79],MSS是每一个TCP报文段中的数据字段的最大长度。数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度。而是“TCP报文段长度减去TCP首部长度”。

TCP可靠传输的实现

以字节为单位的滑动窗口

TCP的滑动窗口是以字节为单位的。现假设A收到了来自B发来的确认报文段,其中窗口是20字节,而确认号是31(这表明B期望收到的下一序号是31,而序号30为止的数据已经收到了)。根据这两个数据,A就构造出自己的发送窗口。
请添加图片描述
我们先讨论发送方A的发送窗口。发送窗口表示:在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。

发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。前面我们提到过,接收方会把自己的接收窗口数值放在窗口字段中发送给对方,因此,A的发送窗口定不能超过B的接收窗口数值。

发送窗口后沿的后面部分表示已发送且已收到了确认。这些数据显然不需要再保留了。而发送窗口前沿的前面部分表示不允许发送的,因为接收方都没有为这部分数据保留临时存放的缓存空间。

发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销已收到的确认。

发送窗口前沿通常是不断向前移动,但也有可能不动,这又分两种情况:一是没有收到新的确认,对方通知的接收窗口大小也不变;二是收到了新的确认但对方通知的接收窗口缩小了,这也会使得A的发送窗口前沿正好不动。

TCP的流量控制

利用滑动窗口实现流量控制

一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失。所谓流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制。

通过下图的例子说明如何利用滑动窗口机制进行流量控制:
请添加图片描述

注:在TCP连接建立时,B会告诉A:“我的接收窗口rwnd=400”,因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。TCP的窗口单位是字节而不是报文段。

TCP的拥塞控制

拥塞控制的一般原理

在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等,都是网络中的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫做拥塞(congestion)。可以把出现网络拥塞的条件改写成如下的关系式:

∑ 对资源的需求 > 可用资源 \sum_{}^{}{对资源的需求 > 可用资源} 对资源的需求>可用资源

网络拥塞往往是由许多因素引起的。例如,当某个结点缓存的容量太小时,到达该结点的分组因无存储空间暂存而不得不被丢弃。现在设想将该结点缓存的容量扩展到非常大,于是凡到达该结点的分组均可在结点的缓存队列中排队,不受任何限制。由于输出链路的容量和处理机的速度并未提高,因此在这队列中的绝大多数分组的排队等待时间将会大大增加,结果上层软件只好把它们进行重传(因为早就超时了)。由此可见,简单地扩大缓存的存储空间同样会造成网络资源的严重浪费,因而解决不了网络拥塞的问题。

又如,处理机处理的速率太慢可能引起网络的拥塞。简单地将处理机的速率提高,可能会使上述情况缓解一些,但往往又会将瓶颈转移到其他地方。问题的实质往往是整个系统的各个部分不匹配,只有所有的部分都平衡了,问题才会得到解决。

拥塞常常趋于恶化。如果一个路由器没有足够的缓存空间,它就会丢弃一些新到的分组,但当分组被丢弃时,发送这一分组的源点就会重传这一分组,甚至可能还要重传多次。这样会引起更多的分组流入网络和被网络中的路由器丢弃。可见拥塞引起的重传并不会缓解网络的拥塞,反而会加剧网络的拥塞。

进行拥塞控制需要付出代价。这首先需要获得网络内部流量分布的信息。在实施拥塞控制时,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外开销。拥塞控制有时需要将一些资源(如缓存、带宽等)分配给个别用户(或一些类别的用户)单独使用,这样就使得网络资源不能更好地实现共享。十分明显,在设计拥塞控制策略时,必须全面衡量得失。

在下图中的横坐标是提供的负载(offered load),代表单位时间内输入给网络的分组数目,因此提供的负载也称为输入负载网络负载。纵坐标是吞吐量(throughput),代表单位时间内从网络输出的分组数目。具有理想拥塞控制的网络,在吞吐量饱和之前,网络吞吐量应等于提供的负载,故吞吐量曲线是45°的斜线。但当提供的负载超过某一限度时,由于网络资源受限,吞吐量不再增长而保持为水平线,即吞吐量达到饱和。这就表明提供的负载中有一部分损失掉了(例如,输入到网络的某些分组被某个结点丢弃了)。虽然如此,在这种理想的拥塞控制下,网络的吞吐量仍然维持在其所能达到的最大值。

请添加图片描述
但是,实际网络的情况就很不相同了。从上图可看出,随着提供的负载的增大,网络吞吐量的增长速率逐渐减小。也就是说,在网络吞吐量还未达到饱和时,就已经有一部分的输入分组被丢弃了。当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态。更值得注意的是,当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了拥塞状态。当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作,这就是所谓的死锁(deadlock)。

实践证明,拥塞控制是很难设计的,因为它是一个动态的(而不是静态的)问题。当前网络正朝着高速化的方向发展,这很容易出现缓存不够大而造成分组的丢失。但分组的丢失是网络发生拥塞的征兆而不是原因。在许多情况下,甚至正是拥塞控制机制本身成为引起网络性能恶化甚至发生死锁的原因。这点应特别引起重视。

由于计算机网络是一个很复杂的系统,因此可以从控制理论的角度来看拥塞控制这个问题。这样,从大的方面看,可以分为开环控制闭环控制两种方法。

开环控制就是在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞(将很多资源设计得足够多,足够大),但一旦整个系统运行起来,就不再中途进行改正了。

闭环控制是基于反馈环路的概念,主要有以下几种措施:

(1) 监测网络系统以便检测到拥塞在何时、何处发生。

(2) 把拥塞发生的信息传送到可采取行动的地方。

(3) 调整网络系统的运行以解决出现的问题。

有很多的方法可用来监测网络的拥塞。主要的一些指标是:由于缺少缓存空间而被丢弃的分组的百分数、平均队列长度、超时重传的分组数、平均分组时延、分组时延的标准差,等等。上述这些指标的上升都标志着拥塞的增长。

一般在监测到拥塞发生时,要将拥塞发生的信息传送到产生分组的源站。当然,通知拥塞发生的分组同样会使网络更加拥塞。

另一种方法是在路由器转发的分组中保留一个比特或字段,用该比特或字段的值表示网络没有拥塞或产生了拥塞。也可以由一些主机或路由器周期性地发出探测分组,以询问拥塞是否发生。

此外,过于频繁地采取行动以缓和网络的拥塞,会使系统产生不稳定的振荡,但过于迟缓地采取行动又不具有任何实用价值。因此,要采用某种折中的方法,但选择正确的时间常数是相当困难的。

TCP的拥塞控制方法

下面来讨论下防止网络拥塞的几个方法。TCP进行拥塞控制的算法有四种:慢开始(slow-start),拥塞避免(congestion avoidance),快重传(fast retransmit)和快恢复(fast recovery)。

下面讨论的拥塞控制也叫做基于窗口的拥塞控制。为此,发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口

发送方控制拥塞窗口的原则是:

只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便把更多的分组发送出去,这样就可以提高网络的利用率。但只要网络出现拥塞或有可能出现拥塞,就必须把拥塞窗口减小一些,以减少注入到网络中的分组数,以便缓解网络出现的拥塞。

那么发送方又是如何知道网络发生了拥塞呢?

我们知道,当网络发生拥塞时,路由器就要丢弃分组。因此只要发送方没有按时收到应当到达的确认报文,也就是说,只要出现了超时,就可以猜想网络可能出现了拥塞。现在通信线路的传输质量一般都很好,因传输出差错而丢弃分组的概率是很小的(远小于1%)。因此,判断网络拥塞的依据就是出现了超时。

慢开始

下面我们讨论的是拥塞窗口cwnd的大小怎样变化。

慢开始算法的思路是这样的:

当主机开始发送数据时,由于并不清楚网络的负荷情况,所以如果立即把大量数据字节注入到网络,那么就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。

旧的规定是这样的:在刚刚开始发送报文段时,先把初始拥塞窗口cwnd设置为1至2个发送方的最大报文段SMSS(Sender Maximum Segment Size)的数值,但新的RFC5681把初始拥塞窗口cwnd设置为不超过2至4个SMSS的数值。

具体的规定如下:

若SMSS>2190字节,则设置初始拥塞窗口cwnd=2 × SMSS字节,且不得超过2个报文段。

若(SMSS>1095字节)且(SMSS≤2190字节),则设置初始拥塞窗口cwnd=3×SMSS字节,且不得超过3个报文段。

若SMsS≤1095字节,则设置初始拥塞窗口cwnd=4×SMSS字节,且不得超过4个报文段。

可见这个规定就是限制初始拥塞窗口的字节数。

慢开始规定,在每收到一个对新的报文段的确认后,可以把拥塞窗口增加最多一个SMSS的数值,可以表述为下式:

拥塞窗口 c w n d 每次的增加量 = m i n ( N , S M S S ) 拥塞窗口cwnd每次的增加量 = min(N, SMSS) 拥塞窗口cwnd每次的增加量=min(N,SMSS)

其中N是原先未被确认的、但现在被刚收到的确认报文段所确认的字节数,不难看出,当N < SMSS,拥塞窗口每次的增加量要小于SMSS。用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理。

下面用例子说明慢开始算法的原理。请注意,虽然实际上TCP是用字节数作为窗口大小的单位,但为叙述方便起见,我们用报文段的个数作为窗口大小的单位,这样可以使用较小的数字来阐明拥塞控制的原理。

在一开始发送方先设置cwnd=1,发送第一个报文段M1,接收方收到后确认M1。发送方收到对M1的确认后,把cwnd从1增大到2,于是发送方接着发送M2和M3两个报文段。接收方收到后发回对M2和M3的确认。发送方每收到一个对新报文段的确认(重传的不算在内)就使发送方的拥塞窗口加1,因此发送方在收到两个确认后,cwnd就从2增大到4,并可发送M4~M7共4个报文段。因此使用慢开始算法后,每经过一个传输轮次(transmission round),拥塞窗口cwnd就加倍。
请添加图片描述
一个传输轮次所经历的时间其实就是往返时间RTT(RTT并非是恒定的数值)。使用“传输轮次”是更加强调:把拥塞窗口cwnd所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。例如,拥塞窗口cwnd的大小是4个报文段,那么这时的往返时间RTT就是发送方连续发送4个报文段,并收到这4个报文段的确认,总共经历的时间。

我们要指出,慢开始的“慢”并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大cwnd。这当然比设置大的cwnd值一下子把许多报文段注入到网络中要“慢得多”。这对防止网络出现拥塞是一个非常好的方法。

顺便指出,上图只是为了说明慢开始的原理。在TCP的实际运行中,其实发送方只要收到一个对新报文段的确认,其拥塞窗口cwnd就可以立即加1,并可以立即发送新的报文段,而不需要等这个轮次中所有的确认都收到后再发送新的报文段。

为了防止拥塞窗口cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh状态变量。慢开始门限 ssthresh的用法如下:

  • 当cwnd< ssthresh时,使用上述的慢开始算法。
  • 当cwnd> ssthresh时,停止使用慢开始算法而改用拥塞避免算法。
  • 当cwnd= ssthresh时,既可使用慢开始算法,也可使用拥塞避免算法。

拥塞避免

拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是像慢开始阶段那样加倍增长。因此在拥塞避免阶段就有“加法增大”AI(Additive increase)的特点。这表明在拥塞避免阶段,拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

下图用具体例子说明了在拥塞控制的过程中,TCP的拥塞窗口cwnd是怎样变化的,图中的的数字 ❶ 至 ❺ 是特别要注意的几个点。现假定TCP的发送窗口等于拥塞窗口。

当TCP连接进行初始化时,把拥塞窗口cwnd置为1。为了便于理解,图中的窗口单位不使用字节而使用报文段的个数。在本例中,慢开始门限的初始值设置为16个报文段,即ssthresh=16。在执行慢开始算法时,发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1,然后开始下一轮的传输(下图中的横坐标是传输轮次而不是时间)。

因此,拥塞窗口cwnd随着传输轮次按指数规律增长。当拥塞窗口cwnd增长到慢开始门限值ssthresh时(图中的点❶,此时拥塞窗cwnd=16),就改为执行拥塞避免算法,拥塞窗口按线性规律增长。但请注意,“拥塞避免”并非完全能够避免了拥塞。“拥塞避免”是说把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。
请添加图片描述
当拥塞窗口cwnd=24时,网络出现了超时(图中的点❷),发送方判断为网络拥塞。于是调整门限值 ssthresh = cwnd/2 = 12,同时设置拥塞窗口cwnd=1,进入慢开始阶段。

按照慢开始算法,发送方每收到一个对新报文段的确认ACK,就把拥塞窗口值加1。当拥塞窗口cwnd= ssthresh=12时(图中的点❸,这是新的 ssthresh值),改为执行拥塞避免算法,拥塞窗口开始按线性规律增大。

当拥塞窗口cwnd=16时(图中的点❹),出现了一个新的情况,就是发送方一连收到3个对同一个报文段的重复确认(图中记为3-ACK)。关于这个问题要解释如下。

有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞。如果发送方迟迟收不到确认,就会产生超时,就会误认为网络发生了拥塞。这就导致发送方错误地启动慢开始,把拥塞窗口cwnd又设置为1,因而降低了传输效率。

采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认。

如下图所示,接收方收到了M1和M2后都分别及时发出了确认。现假定接收方没有收到M3但却收到了M4。本来接收方可以什么都不做。但按照快重传算法,接收方必须立即发送对M2的重复确认,以便让发送方及早知道接收方没有收到报文段M3。发送方接着发送M5和M6。接收方收到后也仍要再次分别发出对M2的重复确认。这样,发送方共收到了接收方的4个对M2的确认,其中后3个都是重复确认。

快重传算法规定,发送方只要一连收到3个重复确认,就知道接收方确实没有收到报文段M3,因而应当立即进行重传(即“快重传”),这样就不会出现超时,发送方也不就会误认为出现了网络拥塞(而是偶然丢包了)。使用快重传可以使整个网络的吞吐量提高约20%。
请添加图片描述
因此,在【TCP拥塞窗口变化图】中的点❹,发送方知道现在只是丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。这时,发送方调整门限值 ssthresh=cwd/2=8,同时设置拥塞窗口cwnd= ssthresh=8,并开始执行拥塞避免算法(【TCP拥塞窗口变化图】中的点❺)。

但请注意,也有的快恢复实现是把快恢复开始时的拥塞窗口cwnd值再增大一些(增大3个报文段的长度),即等于新的 ssthresh+3×MSS。这样做的理由是:既然发送方收到3个重复的确认,就表明有3个分组已经离开了网络,这3个分组不再消耗网络的资源而是停留在接收方的缓存中(接收方发送出3个重复的确认就证明了这个事实)。可见现在网络中并不是堆积了分组而是减少了3个分组,因此可以适当把拥塞窗口扩大些。

从【TCP拥塞窗口变化图】中可以看出,在拥塞避免阶段,拥塞窗口是按照线性规律增大的,这常称为“加法增大”AI( Additive Increase)。而一旦出现超时或3个重复的确认,就要把门限值设置为当前拥塞窗口值的一半,并大大减小拥塞窗口的数值。这常称为“乘法减小”MD(Multiplicative Decrease)二者合在一起就是所谓的AMD算法。根据以上所述,TCP的拥塞控制可以归纳为下面的流程图:
请添加图片描述
发送窗口的大小由网络的拥塞程度来决定,但实际上接收方的缓存空间总是有限的。接收方根据自己的接收能力设定了接收方窗口rwnd,并把这个窗口值写入TCP首部中的窗口字段,传送给发送方。因此,接收方窗口又称为通知窗口(advertised window)。因此,从接收方对发送方的流量控制的角度考虑,发送方的发送窗口一定不能超过对方给出的接收方窗口值rwnd。

如果把拥塞控制和接收方对发送方的流量控制一起考虑,那么很显然,发送方的窗口的上限值应当取为接收方窗口rwnd和拥塞窗口cwnd这两个变量中较小的一个,也就是说:

发送方窗口的上限值  =   M i n [ r w n d , c w n d ] 发送方窗口的上限值 = Min[rwnd, cwnd] 发送方窗口的上限值=Min[rwnd,cwnd]

上式指出:当rwnd<cwnd时,是接收方的接收能力限制发送方窗口的最大值。
反之,当cwnd<rwnd时,则是网络的拥塞程度限制发送方窗口的最大值。
也就是说,rwnd和cwnd中数值较小的一个,控制了发送方发送数据的速率。

TCP的运输连接管理

TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。因此,运输连接就有三个阶段,即连接建立数据传送连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。

在TCP连接建立过程中要解决以下三个问题:

(1) 要使每一方能够确知对方的存在。

(2) 要允许双方协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)。

(3) 能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。

TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户端( client),而被动等待连接建立的应用进程叫做服务器(server)。

TCP的连接建立

TCP建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个TCP报文段,下图画出了三报文握手建立TCP连接的过程。
请添加图片描述
开始,B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。然后服务器进程就处于 LISTEN(收听)状态,等待客户的连接请求。如有,即作出响应。

注:传输控制块TCB(Transmission Control block)存储了每一个连接中的一些重要信息,如:TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号,等等。

A的TCP客户进程也是首先创建传输控制模块TCB。然后,在打算建立TCP连接时向B发出连接请求报文段,这时首部中的同步位SYN=1,同时选择一个初始序号seq=x。TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入 SYN-SENT(同步已发送)状态B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。

请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入 SYN-RCVD(同步收到)状态。

TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq时,TCP连接就已经建立,A进入 ESTABLISHED(已建立连接)状态。

当B收到A的确认后,也进入 ESTABLISHED状态。

上面给出的连接建立过程叫做三报文握手

那么为什么A最后还要发送一次确认呢? 这主要是为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。

这所谓“已失效的连接请求报文段”是这样产生的。考虑一种正常情况,A发出连接请求,但因连接请求报文丢失而未收到确认。于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。A共发送了两个连接请求报文段,其中第一个丢失,第二个到达了B,没有“已失效的连接请求报文段”。

现假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达B。本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用报文握手,那么只要B发出确认,新的连接就建立了。

由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。

采用三报文握手的办法,可以防止上述现象的发生。例如在刚才的异常情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。

TCP的连接释放

TCP连接释放过程比较复杂,我们仍结合双方状态的改变来阐明连接释放的过程。

数据传输结束后,通信的双方都可释放连接。现在A和B都处于 ESTABLISHED状态。A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的终止控制位FN置1,其序号seq=u,它等于前面已传送过的数据的最后一个字节的序号加1。
这时A进入 FIN-WAIT-1(终止等待1)状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
请添加图片描述
B收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1。然后B就进入 CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发送了,但B若发送数据,A仍要接收。也就是说,从B到A这个方向的连接并未关闭,这个状态可能会持续一段时间。

A收到来自B的确认后,就进入 FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。

若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack=u+1。这时B就进入 LAST-ACK(最后确认)状态,等待A的确认。

A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到 TIME- WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,A才进入到 CLOSED状态。

时间MSL叫做最长报文段寿命(Maximum Segment Lifetime),RFC793建议设为2分钟。但这完全是从工程上来考虑的,对于现在的网络,MSL=2分钟可能太长了一些。因此TCP允许不同的实现可根据具体情况使用更小的MSL值。因此,从A进入到 TIME- WAIT状态后,要经过4分钟才能进入到 CLOSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。

为什么A在TIME-WAIT状态必须等待2MSL的时间呢?这有两个理由。

第一,为了保证A发送的最后一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在 LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着A重传一次确认,重新启动2MSL计时器。最后,A和B都正常进入到CLOSED状态。如果A在 TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后立即释放连接,那么就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常步骤进入 CLOSED状态。

第二,防止前面提到的“已失效的连接请求报文段”出现在本连接中。A在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

B只要收到了A发出的确认,就进入 CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次的TCP连接。我们注意到,B结束TCP连接的时间要比A早一些。

上述的TCP连接释放过程是四报文握手。

除时间等待计时器外,TCP还设有一个保活计时器(keepalive timer)。设想有这样的情况:客户已主动与服务器建立了TCP连接。但后来客户端的主机突然出故障。显然,服务器以后就不能再收到客户发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就是使用保活计时器。服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒钟发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个连接。

TCP的有限状态机

为了更清晰地看出TCP连接的各种状态之间的关系,下图给出了TCP的有限状态机。图中每一个方框即TCP可能具有的状态。每个方框中的大写英文字符串是TCP标准所使用的TCP连接状态名。状态之间的箭头表示可能发生的状态变迁,箭头旁边的字,表明引起这种变迁的原因,或表明发生状态变迁后又出现什么动作。请注意图中有三种不同的箭头。粗实线箭头表示对客户进程的正常变迁。粗虚线箭头表示对服务器进程的正常变迁。另一种细线箭头表示异常变迁。
请添加图片描述




参考:1.《计算机网络(第7版)》·谢希仁
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值