计算机网络抄手 运输层

news2025/2/22 3:34:20

一、运输层协议概述

1. 进程之间的通信

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

下图说明运输层的作用。设局域网LAN1上的主机A和局域网LAN2上的主机B通过互连的广域网WAN进行通信。IP协议能够把源主机A发送出的分组,按照首部中的目的地址,送交到目的主机B,那么,为什么还需要运输层呢?

IP层来说,通信的两端是两台主机。IP数据报的首部明确地标志了这两台主机的IP地址。但”两台主机之间的通信“这种说法不够明确。真正进行通信的实体是在两台主机中的哪个构件呢?是主机中的应用进程,是一台主机中的应用进程和另一台主机中的应用进程在交换数据(通信)。严格来讲,两台主机进行通信就是两台主机中的应用进程互相通信IP协议虽然能把分组送到目的主机,但是这个分组还停留在主机的网络层而没有交付主机的应用进程。

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

下图中,主机A的应用进程AP1和主机B的应用进程AP3通信,与此同时,应用进程AP2也和对方的应用进程AP4通信。这表明运输层有一个很重要的功能-复用分用。”复用“是指在发送方不同的应用进程都可以使用同一个运输层协议传送数据(当然需要加上适当的首部),而”分用“是指接收方的运输层在剥去报文的首部后能够把这些数据正确交付目的应用进程。

下图中的两个运输层之间有一个深色双向粗箭头,写明”运输层提供应用进程间的逻辑通信“。”逻辑通信“的意思是:从应用层来看,只要把应用层报文交给下面的运输层,运输层就可以把这报文传送到对方的运输层,好象这种通信就是沿水平方向直接传送数据。但事实上这两个运输层之间并没有一条水平方向的物理连接。数据的传送是沿着图中的虚线方向(经过多个层次)传送的。”逻辑通信“的意思是”好像是这样通信,但事实上并非真的这样通信“。

这里可以看出网络层和运输层有明显区别。网络层为主机之间的通信提供服务,而运输层则在网络层的基础上,为应用进程之间的通信提供服务。然而正如后面要讨论的,运输层还具有网络层无法代替的许多其他功能。

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

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

运输层向高层用户屏蔽了下面网络核心的细节(如网络拓扑、所采用的路由选择协议等),它使应用进程看见的就是好像在两个运输层实体之间有一条端到端的逻辑通信信道,但这条逻辑通信信道对上层的表现却因运输层使用的不同协议而有很大的差别。当运输层采用面向连接的TCP协议时尽管下面的网络是不可靠的(只提供尽最大努力服务),但这种逻辑通信信道就相当于一条全双工的可靠信道。但当运输层采用无连接的UDP协议时,这种逻辑通信信道仍然是一条不可靠信道


2. 运输层的两个主要协议

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

(1)用户数据报协议UDP(User Datagram Protocol)[RFC 768,STD6]

(2)传输控制协议TCP(Transmission Control Protocol)[RFC 793,STD7]

下图给出了这两种协议在协议栈中的位置。

按照OSI的术语,两个对等运输实体在通信时传送的数据单位叫作运输协议数据单元TPDU(Transport Protocol Data Unit)。但在TCP/IP体系中,则根据所使用的协议是TCP或UDP,分别称之为TCP报文段UDP用户数据报

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

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

下标给出了一些应用和应用层协议主要使用的运输层协议(UDP 或TCP)。


3. 运输层的端口

运输层的复用分用功能。其实日常生活中也有很多复用和分用的例子。假定一个机构的所有部门向外单位发出的公文都由收发室负责寄出,这相当于各部门都”复用“这个收发室。当收发室收到从外单位寄来的公文时,则要完成”分用“功能,即按照信封上写明的本机构的部门地址把公文正确进行交付。

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

在单个计算机中的进程是用进程标识符(一个不大的整数)来标志的。但是在互联网环境下,用计算机操作系统所指派的这种进程标识符来标志运行在应用层的各种应用进程则是不行的。因为在互联网上使用的计算机的操作系统种类很多,而不同的操作系统又使用不同格式的进程标识符。为了使运行不同操作系统的计算机的应用进程能够互相通信,就必须用统一的方法(而这种方法必须与特定操作系统无关)对TCP/IP体系的应用进程进行标志。

但是,把一个特定机器上运行的特定进程,指明为互联网上通信的最后终点是不可行的。这是因为进程的创建和撤销都是动态的,通信的一方无法知道和识别对方机器上的进程。另外,我们往往需要利用目的主机提供的功能来识别终点,而不需要知道具体实现这个功能的进程是哪一个。

在应用层和运输层之间的界面上,设置一个特殊的抽象的”门“。应用层中的应用进程要通过运输层发送到互联网,必须要通过这个门。而别的主机上的应用进程要寻找本主机中的某个应用进程,也必须通过这个门。这样,就可以把应用层和运输层的界面上这些”门“,设为通信的抽象终点。这些抽象终点的正式名称就是协议端口(protocol port),一般简称为端口(port)。每一个端口用一个称为端口号(port number)的正整数来标志。主机的操作系统提供了接口机制,使得进程能够通过这种机制找到所要找的端口。

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

当应用层要发送数据时,应用进程就把数据发送到适当的端口,然后运输层从该端口读取数据,进行后续的处理(把数据发送到目的主机)。当运输层收到对方主机发来的数据时,就把数据发送到适当的端口,然后应用进程就从该端口读取这些数据。显然,端口必须有一定容量的缓存来暂时存放数据。

后面的UDPTCP的首部格式中(图5-5,图5-14),都有源端口目的端口这两个重要字段。这两个端口就是运输层和应用层进行交互的地点。

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

由此可见,两个计算机中的进程要通信,不仅需要知道对方的IP地址(找到对方的计算机),而且要知道对方的端口号(找到对方计算机中的应用进程)。这和寄信的过程类似,写信时填地址(相当于IP地址),还有收件人的姓名(相当于端口号)。信封上还要写自己的地址和姓名。收信人回信时,很容易在信封上找到发信人的地址和姓名。互联网上的计算机通信采用客户-服务器方式。客户在发起通信请求时,必须先知道对方服务器的IP地址和端口号。因此运输层的端口号分为以下两大类。

(1)服务器端使用的端口号

这里又分为两类,最重要的一类叫作熟知端口号(well-known port number)或全球通用端口号,数值为0~1023。这些熟知端口号最初公布在文档中[RFC 1700,STD2],但后来因为互联网发展太快,这种标准文档无法随时更新,因此在RFC3232中就把RFC1700列为陈旧的,而当前最新的熟知端口号可在网址www.iana.org上查到。IANA把这些熟知端口号指派给了TCP/IP最重要的一些应用程序,让所有用户都知道。当一种新的应用程序出现后,IANA必须为它指派一个熟知端口,否则在互联网上的其他应用进程就无法和它进行通信。和电话通信相比,熟知端口号相当于所有人都知道的应急电话120,110等。下标给出了一些常用的熟知端口号。

另一类叫作登记端口号,数值为1024~49151。这类端口号是为没有熟知端口号的应用程序使用的。要使用这类端口号必须在IANA按照规定的手续登记,以防止重复。

(2)客户端使用的端口号

数值为49152~65535。由于这类端口号仅在客户进程运行时才动态选择,因此又叫作短暂端口号。这类端口号就是临时端口号,留给客户进程选择临时使用。当服务器进程收到客户进程的报文后,就知道了客户进程所使用的端口号,因而可以把数据发送给客户进程。通信结束后,刚才已使用过的客户端口号就被系统收回,以便给其他客户进程使用。


二、用户数据报协议UDP

1. 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的通信和端口号的关系(如下图所示)。主机H1中有三个应用进程分别要和主机H2中的两个应用进程进行通信。通信双方的关系是:P1->P4,P2->P4,P3->P5。主机H1的操作系统为这三个进程分别指派了端口,其端口号分别为a,b和c。图中位于应用层和运输层之间的小方框代表端口。在端口小方框中间还画有队列,表示端口具有缓存的功能,可以把收到的数据暂时存储一下。有时也可以把队列画成双向的,即分别表示存放来自应用层或运输层的数据。现在假定主机H1中的进程已经知道了对方进程P4和P5的端口号分别为x和y,于是在主机H1的运输层就可以组装成需要发送的UDP用户数据报,其中最重要的地址信息(源端口,目的端口)分别是(a,x),(b,x)和(c,y)。下图中把运输层以下的都省略了。因此,进程之间的通信现在可以看成是两个端口之间的通信。

图中在两个运输层之间有一条虚线,表示在两个运输层之间可以进行通信,而不是在一条连接。但这种通信是不可靠的通信,即所发送的报文在传输过程中有可能丢失,同时也不保证报文都能按照发送的先后顺序到达终点。这正是UDP通信的特点:简单方便,但不可靠。如果想要得到可靠地运输层通信,那就要使用TCP进行通信。注意,在两个运输层的UDP之间没有建立连接。

上图的例子画出了多对一的通信(a->x,b->x)。如果改成a->x,a->y,则是一对多的情况了。

主机H1中的3个应用进程,把用户数据通过各自的端口传送到了运输层后,就共用一个网络层协议,把收到的UDP用户数据报组装成不同的IP数据报,发送到互联网。这就是UDP的复用功能。主机H2的网络层收到3个IP数据报后,提取出数据部分(即UDP用户数据报),然后根据其首部中的目的端口号,分别传送到相应的端口,以便上层的应用进程到端口读取数据。这就是UDP的分用功能。

虽然某些实时应用需要使用没有拥塞控制的UDP,但当很多的源主机同时都向网络发送高速率的实时视频流时,网络就有可能发生拥塞,结果大家都无法正常接收。因此,不使用拥塞控制功能的UDP有可能会引起网络产生严重的拥塞问题。

还有一些使用UDP的实时应用,需要对UDP的不可靠的传输进行适当的改进,以减少数据的丢失。这种情况下,应用进程本身可以在不影响应用的实时性的前提下,增加一些提高可靠性的措施,如采用前向纠错或重传已丢失的报文。


2. UDP的首部格式

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

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

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

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

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

如果接收方UDP发现收到的报文中目的端口号不正确(即不存在对应于该端口号的应用进程),就丢弃该报文,并由网际控制协议ICMP发送“端口不可达”差错报文给发送方。上一章的”ICMP的应用举例“谈论traceroute时,就是让发送的UDP用户数据报故意使用一个非法的UDP端口,结果ICMP就返回”端口不可达“差错报文,因而达到了测试的目的。

UDP用户数据报首部中检验和的计算方法有些特殊。在计算检验和时,要在UDP用户数据报之前增加12个字节的伪首部。所谓”伪首部“是因为这种伪首部并不是UDP用户数据报真正的首部。只是计算检验和时,临时添加在UDP用户数据报前面,得到一个临时的UDP用户数据报。检验和就是按照这个临时的UDP用户数据报来计算的。伪首部既不向下传送也不向上递交,而仅仅是为了计算检验和。上图的最上面给出了伪首部各字段的内容。

UDP计算检验和的方法和计算IP数据报首部检验和的方法相似。但不同的是:IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分一起都检验。在发送方,首先是先把全零放入检验和字段。再把伪首部以及UDP用户数据报看成是由许多16位的字串接起来的。若UDP用户数据报的数据部分不是偶数个字节,则要填入一个全零字节(但此字节不发送)。然后按二进制反码计算出这些16位字的和。将此和的二进制反码写入检验和字段后,就发送这样的UDP用户数据报。在接收方,把收到的UDP用户数据报连同伪首部(以及可能的填充全零字节)一起,按二进制反码求这些16位字的和。当无差错时其结果应为全1。否则就表明有差错出现,接收方就应丢弃这个UDP用户数据报(也可以上交给应用层,但附上出现了差错的警告)。下图给出了一个计算UDP检验和的例子。这里假定用户数据报的长度是15字节,因此要添加一个全0的字节。这种简单的差错检验方法的检错能力并不强,但它简单,处理起来较快。

上图所示,伪首部的第3字段是全零;第4字段是IP首部中的协议字段的值。以前讲过,对于UDP,此协议字段值为17;第5字段是UDP用户数据报的长度。因此,这样的检验和,既检查了UDP用户数据报的源端口号和目的端口号以及UDP用户数据报的数据部分,有检查了IP数据报的源IP地址和目的地址。


三、传输控制协议TCP概述

1. TCP最主要的特点

TCP是TCP/IP体系中非常复杂的一个协议。TCP最主要的特点:

(1)TCP是面向连接的运输层协议。这就是说,应用程序在使用TCP协议之前,必须先建立TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。也就是说,应用进程之间的通信好像在”打电话“:通话前要先拨号建立连接,通话结束后要挂机释放连接。

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

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

(4)TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据存入缓存,上层的应用进程在合适的时候读取缓存中的数据。

(5)面向字节流。TCP中的”“指的是流入到进程或从进程流出的字节序列。”面向字节流“的含义是”:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据仅仅堪称是一连串的无结构的字节流。TCP并不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系(例如,发送方应用程序交给发送方的TCP共有10个数据块,但接收方的TCP可能只用了4个数据块就把收到的字节流交付上层的应用程序)。但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一致。当然,接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据。下图是上述概念的示意图。

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

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


2. TCP的连接

TCP把连接作为最基本的抽象。TCP的许多特性都与TCP是面向连接的这个基本特性有关。

每一条TCP连接有两个端点。那么,TCP连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是运输层的协议端口。TCP连接的端点叫作套接字(socket)插口。根据RFC793的定义:端口号拼接到(concatenated withIP地址即构成了套接字。因此,套接字的表示方法在点分十进制的IP地址后面写上端口号,中间用冒号或逗号隔开。例如,若IP地址是192.3.4.5而端口号是80,那么得到的套接字就是(192.3.4.5:80)。总之,有:

每一条TCP连接唯一地被通信两端的两个端点(即套接字对 socket pair)所确定。即:

这里IP1和IP2分别是两个端点主机的IP地址,而port1和port2分别是两个端点主机中的端口号。因此,TCP连接就是两个套接字socket1和socket2之间的连接。套接字socket是个很抽象的概念。

总之,TCP连接就是由协议软件所提供的一种抽象。虽然有时为了方便,也可以说,在一个应用进程之间建立了一条TCP连接,但一定要记住:TCP连接的端点是个很抽象的套接字,即(IP地址:端口号)。记住:同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以出现在不同的TCP连接中。

本来socket的意思是插座(或插口)。选用socket这个名词是相当准确的。其实一条TCP连接就像一条电缆线,其两端都各带有一个插头。把每一端的插头插入位于主机的应用层和运输层之间的插座后,连个主机之间的进程就可以通过这条电缆线进行可靠通信了。但插座这个名词很容易让人想起来是个硬件,而socket是个软件名词,这样”套接字“就成了socket的标准译名了。

注意,socket这个名词有时容易使人把一些概念混淆,因为随着互联网的不断发展以及网络技术的进步,同一个名词socket可表示多种不同的意思。例如:

(1)允许应用程序访问连网协议的应用编程接口API,即运输层和应用层之间的一种接口,称为socket API,简称为socket。

(2)socket API中使用的一个函数名也叫作socket。

(3)调用scoket函数的端点称为socket,如”创建一个数据报socket“。

(4)调用socket函数时,其返回值称为socket描述符,可简称为socket。

(5)在操作系统内核中连网协议的Berkeley实现,称为socket实现


四、可靠传输的工作原理

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

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

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

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

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

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


1. 停止等待协议

全双工通信的双方既是发送方也是接收方。为方便讨论,仅考虑A发送数据而B接收数据并发送确认。因此A叫作发送方,而B叫作接受方。因为这里讨论可靠传输的原理,因此把传送的数据单元都称为分组,而并不考虑数据是在哪一个层次上传送的。“停止等待”就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。


1. 无差错情况

停止等待协议可用下图说明。下图a是最简单的无差错情况。A发送分组M1,发完就暂停发送,等待B确认。B收到了M1就向A发送确认。A在收到了对M1的确认后,就再发送下一个分组M2。同样,在收到B对M2的确认后,再发送M3。

2. 出现差错

上图b是分组在传输过程中出现差错的情况。B接收M1时检测出了差错,就丢弃了M1,其他什么也不做(不通知A收到有差错的分组)。也可能是M1在传输过程中丢失了,这时B什么都不知道。在这两种情况下,B都不会发送任何信息。可靠传输协议是这样设计的:A只要超过一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫作超时重传。要实现超时重传,就要在每发送完一个分组时设置一个超时计时器。如果在超时计时器到期之前收到了对方的确认,就撤销已设置的超时计时器。其实图a中,A为每一个已发送的分组都设置了一个超时计时器。但A只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器。

这里注意三点:

(1)A在发送完一个分组后,必须暂时保留已发送的分组的副本(在发送超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本。

(2)分组和确认分组都必须进行编号。这样才能明确哪一个发送出去的分组收到了确认,哪一个分组没有确认。

(3)超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。上图b中的一段虚线表示如果M1正确到达B同时A也正确收到确认的过程。可见重传时间应设定为比平均往返时间更长一些。显然,重传时间设定得很长,那么通信的效率就会很低。但如果重传时间设定得太短,以致产生不必要得重传,就浪费了网络资源。然而,在运输层重传时间得准确设定非常复杂,因为已发送的分组到底会经过哪些网络,以及这些网络将会产生多大的时延(这取决于这些网络当时的拥塞情况)。下图中把往返时间当作固定的(显然不符合网络的实际情况),只是为了讲述原理的方便。

3. 确认丢失和确认迟到

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

第一,丢弃这个重复的分组M1,不向上层重复交付。

第二,向A发送确认。A之所以重传,是因为A没有收到确认。

上图b也是一种情况的出现。传输过程中没有出现差错,但B对分组M1的确认迟到了。A会受到重复的确认。对重复的确认的处理很简单:收下后丢弃,但什么也不做。B仍然会收到重复的M1,并且同样要丢弃重复的M1,并重传确认分组。

通常A最终总是可以收到对所有发出的分组的确认。如果A不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信。

使用上述的确认和重传机制,就可以在不可靠的传输网络上实现可靠的通信

像上述的这种可靠传输协议常称为自动重传请求ARQ(Automatic Repeat reQuest)。意思是重传的请求是自动进行的,因此也可见自动请求重传这样的译名。接收方不需要请求发送方重传某个出错的分组。

4. 信道利用率

停止等待协议的优点是简单,但缺点是信道利用率太低。下图说明了这个问题。简单起见,假定在A和B之间有一条直通的信道来传送分组。

假定A发送分组需要的时间是TD。显然。TD等于分组长度除以数据率。再假定分组正确到达B后,B处理分组的时间可以忽略不计,同时立即发回确认。假定B发送确认分组需要时间TA。如果A处理确认分组的时间也可以忽略不计,那么A在经过时间(TD+RTT+TA)后就可以再发送下一个分组,这里的RTT是往返时间。因为仅仅是在时间TD内采用来传送有用的数据(包括分组的首部),因此信道的利用率U可用下式计算:

注意,更细致的计算还可以在上式分子的时间TD内扣除传送控制信息(如首部)所花费的时间。但在进行粗略计算时,用近似的式就可以了。

上式中的往返时间RTT取决于所使用的信道。例如,假定1200KM的信道往返时间RTT=20ms,分组长度是1200bit,发送速率是1Mbit/s。若忽略处理时间和TA(TA一般远小于TD),则可算出信道的利用率U = 5.66%。但若把发送速率提高到10Mbit/s,则U = 5.96 * 10^{-3}。信道在绝大多数时间内都是空闲的。

上图可看出,当往返时间RTT远大于分组发送时间TD时,信道的利用率会非常低。还应注意,上图并没有考虑出现差错后的重传分组。若出现重传,则对传送有用的数据信息来说,信道的利用率还要降低。

为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输(下图所示)。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据在不间断地传送。显然,这种传输方式可以获得很高的信道利用率。

使用流水线传输时,就要使用下面的连续ARQ协议滑动窗口协议


2. 连续ARQ协议

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

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

在讨论滑动窗口时,应注意,图中还有一个时间坐标(以后往往省略这样的时间坐标)。按习惯,“向前”是指“向着时间增大的方向”,而“向后”则是“向着时间减少的方向”。分组发送是按照分组序号从小到大发送的。

连续ARQ协议规定,发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。上图b表示发送方收到了对第1个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。

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

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

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


五、TCP报文段的首部格式

TCP虽然是面向字节流的,但TCP传送的数据单元却是报文段。一个TCP报文段分为首部数据两部分,而TCP的全部功能都体现在它首部中个字段的作用

TCP报文段首部的前20个字节是固定的(如下图所示),后面有4n字节是根据需要而增加的选项。因此TPC首部的最小长度是20字节。

首部固定部分各字段的意义:

(1)源端口目的端口

各占2个字节,分别写入源端口号和目的端口号。和前面图5-5所示的UDP的分用相似,TCP的分用功能也是通过端口实现的。

(2)序号

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

(3)确认号

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

总之,记住:

若确认号 = N,则表明:到序号N - 1为止的所有数据都已正确收到。

由于序号字段有32位长,可对4GB(4千兆字节)的数据进行编号。在一般情况下可保证当序号重复使用时,旧序号的数据早已通过网络达到终点了。

(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就把紧急数据插入到本报问段数据的最前面,而在紧急数据后面的数据仍是普通数据。这是要与首部中紧急指针字段配合使用。

然而在紧急指针字段的具体实现上,由于过去的有些文档有错误或有不太明确的地方,这就导致人们对有关的RFC文档产生了不同的理解。于是,2011年公布的建议标准RFC6093,对紧急指针字段的使用方法做出了更加明确的解释,并更新了几个重要的RFC文档,如RFC793,RFC1011,RFC1122等。

(7)确认ACK(ACKnowledgment)

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

(8)推送PSH(PuSH)

当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送操作。这时,发送方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就表示这是一个连接请求连接接收报文

(11)终止FIN(FINish)

用来释放一个连接。当FIN=1时,表明此报文的发送方的数据已发送完毕,并要求释放运输链接。

(12)窗口

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

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

总之,应记住:

窗口字段明确指出了现在允许对方发送的数据量。窗口值经常再动态地变化着。

(13)检验和

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

(14)紧急指针

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

(15)选项

长度可变,最长可达40字节。当没有使用”选项“时,TCP的首部长度是20字节。最后的填充字段仅仅是为了使整个TCP首部字段是4字节的整数倍。

TCP最初只规定了一种选项,即最大报文段长度MSS(Maximum Segment Size)[RFC 6691]。注意MSS这个名词的含义。MSS是每一个TCP报文段中的数据字段的最大长度。数据字段加上TCP首部才等于整个的TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是”TCP报文段长度减去TCP首部长度“。

为什么要规定一个最大报文段长度MSS呢?这并不是考虑接收方的接收缓存可能放不下TCP报文段中的数据。实际上,MSS与接收窗口值没有关系。TCP报文段的数据部分,至少要加上40字节的首部(TCP首部20字节和IP首部20字节,这里都还没有考虑首部中的选项部分),才能组装成一个IP数据报。若选择较小的MSS长度,网络的利用率就降低了。设想在极端的情况下,当TCP报文段只含有1字节的数据时,在IP层传输的数据报的考校至少有40字节(包括TCP报文段的首部和IP数据报的首部)。这样,对网络的利用率就不会超过1/41。到了数据链路层还要加上一些开销。但反过来,若TCP报文段非常长,那么在IP层传输时就有可能要分解成多个短数据报片。在终点要把收到的各个短数据报片装配成原来的TCP报文段。当传输出错时还要进行重传。这些也都会使开销增大。

因此,从提高网络传输效率考虑,MSS应尽可能大些,只要在IP层传输时不需要再分片就行。由于IP数据报所经历的路径是动态变化的,因此在某条路径上确定的不需要分片的MSS,如果改走另一条路径就可能需要进行分片。因此最佳的MSS实际上是很难确定的。在连接建立的过程中,双方都把自己能够支持的MSS写入这一字段,以后就按照这个数值传送数据,两个传送方向可以有不同的MSS值。若主机未填写这一项,则MSS的默认值是536字节(这个数值来自576字节的IP数据报总长度减去TCP和IP的固定首部)。因此,所有互联网上的主机都应能接收的报文段长度是536+20(固定首部长度)=556字节。

随着互联网的发展,又陆续增加了几个选项,如窗口扩大选项、时间戳选项等(见建议标准RFC 7323)。以后又增加了有关选择确认(SACK)选项(RFC 2018)。这些选项的位置都在图5-13所示的选项字段中。

窗口扩大选项是为了扩大窗口。TCP首部中窗口字段长度是16位,因此最大的窗口大小为64KB.虽然这对早期的网络是足够用的,但对于包含卫星信道的网络,其传播时延和带宽都很大,要获得高吞吐率需要更大的窗口大小。

窗口扩大选项占3字节,其中有一个字节表示移位值S。新的窗口值等于TCP首部中的窗口位数从16增大到(16 + S)。移位值允许使用的最大值是14,相当于窗口最大值增大到2^{16 + 14} - 1 = 2^{30} - 1。

窗口扩大选项可以在双方初始建立TCP连接时进行协商。如果连接的某一段实现了窗口扩大,当它不需要再扩大其窗口值时,可发送S = 0的选项,使窗口大小回到16。

时间戳选项占10字节,其中最主要的字段是时间戳值字段(4字节)和时间戳回送回答字段(4字节)。时间戳选项有以下两个功能:

第一,用来计算往返时间RTT。发送方再发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段值复制到时间戳送回答字段。因此,发送方在收到确认报文后,可以准确计算出RTT。

第二,用于处理TCP序号超过2^{32}的情况,这又称为防止序号绕回PAWS。TCP报文段的序号只有32位,而每增加2^{32}个序号就会重复使用原来用过的序号。当使用高速网络时,在一次TCP连接的数据传送中序号很可能会被重复使用。例如,当使用1.5Mbit/s的速率发送报文段时,序号重复要6小时以上。但若用2.5Gbit/s的速率发送报文段,则不到14秒序号就会重复。为了使接收方能够把新的报文段和迟到很久的报文段区分开,可以在报文段加上这种时间戳。


六、TCP可靠传输的实现

为讲述可靠传输原理的方便,假定数据传输只在一个方向进行,即A发送数据,B给出确认。这样的好处是使讨论限于两个窗口,即发送方A的发送窗口和接收方B的接收窗口。如果再考虑B也向A发送数据,那么还要增加A的接收窗口和B的发送窗口,这样总共有4个都不断在变化大小的窗口。这样讲述帮助不大,且烦琐。


1. 以字节为单位的滑动窗口

TCP的滑动窗口是以字节为单位的。为方便说明滑动窗口的工作原理,故意把后面5-14到5-17图中的字节编号都取得很小(时间窗口大小多为数千字节)。现假定A收到了B发来的确认报文段,其中窗口是20字节,而确认号是31(这表明B期望收到的下一个字节序号是31(注意,这里不是分组的序号),而到序号30为止的数据已经收到了)。根据这两个数据,A就构造出自己的发送窗口,如图5-14所示。

先讨论发送方A的发送窗口。发送窗口表示:在没有收到B的确认情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。

发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。接收方会把自己的接收窗口数值放在窗口字段发送给对方。因此,A的发送窗口一定不能超过B的接收窗口数值。后面会讨论,发送方的发送窗口大小还要受到当时网络拥塞程度的约束。目前不考虑。

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

发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种可能,即不动(没有受到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。发送窗口前沿通常是不断向前移动的,但也有可能不动。这对应两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动。

发送窗口前沿也有可能向后收缩。这发送在对方通知的窗口缩小了。但TCP的标准强烈不赞成这样做。因为很可能发送方在收到这个通知以前已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送数据,会产生一些错误。

现在假定A发送了序号31~41的数据。这时,发送窗口位置并未改变(如图5-15所示),但发送窗口内靠后面有11个字节(灰色方框表示)表示已发送但未收到确认。而发送窗口内靠前面的9个字节(序号42~50)是允许发送但尚未发送的。

上述可看出,要描述一个发送窗口的状态需要三个指针:P1,P2,P3(如图5-15所示)。指针都指向字节的序号。A的发送窗口中三个指针指向的几个部分的意义如下:

P1之前的数据(序号<31)是已发送并已收到确认的部分。

P3之后的数据(序号>50)是不允许发送的部分。

P3-P1=A的发送窗口 = 20(序号31~50).

P2-P1=已发送但尚未收到确认的字节数(序号31~41).

P3-P2=允许发送但当前尚未发送的字节数(序号42~50)(又称为可用窗口有效窗口).

再看一下B的接收窗口。设B的接收窗口大小是20。在接收窗口外面,到序号为30的数据是已经发送过确认,并且已经交付主机了。因此在B可以不再保留这些数据。接收窗口内的数据(序号31~50)是允许接收的。图5-15中,B收到了序号为32和33的数据,但序号为31的数据没有收到(也许丢失了)。注意,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号仍然是31(即期望收到的序号)。

现在假定B收到了序号为31的数据,把序号为31~33的数据交付主机,删除这些数据。接着把接收窗口向前移动3个序号(如图5-16所示),同时给A发送确认,其中窗口值仍为20,但确认号是34.这表明B已经收到了到序号33为止的数据。可注意到,B还收到了序号37,38和40的数据,但这些数据都没有按序到达,只能先暂存在接收窗口中。A收到B的确认后,就可以把发送窗口向前滑动3个序号,但指针P2不动。可以看出,现在A的可用窗口增大了些,可发送的序号范围是42~53.

A在继续发送完序号42~53的数据后,指针P2向前移动和P3重合。发送窗口内的序号都已用完,但还没有再收到确认(如图5-17所示)。由于A的发送窗口已满,可用窗口已减小到零,因此必须停止发送。注意,存在下面这种可能性,就是发送窗口内所有的数据都以正确到达B,B也早已发出了确认。但不幸的是,所有这些确认都滞留在网络中。在没有收到B的确认时,为了保证可靠传输,A只能认为B还没有收到这些数据。于是,A在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到B的确认为止。如果A按序收到落在发送窗口内的确认号,那么A就可以使发送窗口继续向前滑动,并发送新的数据。

在前面5-7中曾给出这样的概念:发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。下面就进一步讨论前面讲的窗口和缓存的关系。图5-18画出了发送方维持的发送缓存和发送窗口,以及接收方维持的接收缓存和接收窗口。这里要明确两点:

第一,缓存空间和序号空间都是有限的,而且都是循环使用的。最好是把它们画成圆环形状的。但这里为了方便,还是把它们画成了长条状。

第二,由于缓存或窗口中实际的字节数可能很大,因此图5-18仅仅是个示意图,没有标出具体的数值。但用这样的图来说明缓存和发送窗口以及接收窗口的关系是很清楚的。

首先看图a所示的发送方的情况。

发送缓存用来暂时存放:

(1)按序到达的、但尚未被接受应用程序读取的数据;

(2)未按序到达的数据。

如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。

图b还指出了下一个期望收到的字节号。这个字节号也就是接收方给发送方的报文段的首部中的确认号。

根据以上讨论,还要强调三点:

第一,虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不是总和B的接收窗口一样大。这是因为通过网络传送窗口值需要经理一定的时间滞后(这个时间不确定)。另外,后面要讲的,发送方A还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值。

第二,对于不按序到达的数据应如何处理,TCP标准并无明确规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利(因为发送方会重复传送较多的数据)。因此TCP通常是把不按序到达的数据先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程

第三,TCP要求接收方必须有累计确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但注意两点。一是接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,反而浪费了网络资源。TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认[RFC 1122, STD3]。二是捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上传送数据。

TCP通信是全双工通信。通信中的每一方都在发送和接收报文段。因此,每一方都有自己的发送窗口和接收窗口。在谈窗口时,要弄清是哪一方的窗口。


2. 超时重传时间的选择

TCP的发送方在规定的时间内没有收到确认就要重传已发送的报文段。概念简单,但重传时间的选择确实TCP最复杂的问题之一。

由于TCP的下层是互联网环境,发送的报文段可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个IP数据报所选择的路由还可能不同。如果把超时重传时间设置太短,就会引起很多报文段的不必要的重传,使网络负荷增大。但若把超时重传时间设置得过长,则又使网络得空闲时间增大,降低了传输效率。

那么,运输层得超时计时器得超时重传时间应如何设置呢?

TCP采用了一种自适应算法,它记录了一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间RTT。TCP保留了RTT的一个加权平均往返时间RTTs(又称为平滑的往返时间,S表示Smoothed。因为进行的是加权平均,因此得出的结果更加平滑)。每当第一次测量到RTT样本时,RTTs值就取为所测量到的RTT样本值。但以后每测量到一个新的RTT样本,就按下式重新计算一次RTTs:

在上式中,0\leqα<1。若α很接近于零,表示新的RTTs的值和旧的RTTs值相比变化不大,而对新的RTT样本影响不大(RTT值更新较慢)。若选择α接近于1,则表示新的RTTs值受新的RTT样本的影响较大(RTT值更新较快)。已称为建议标准的RFC6298推荐的α值为1/8,即0.125。用这种方法得出的加权平均往返时间RTTs就比测量出的RTT值更加平滑。

显然,超时计时器设置的超时重传时间RTO应略大于上面得出的加权平均往返时间RTTs。RFC6298建议这样计算RTO:

而RTTD是RTT的偏差的加权平均值,它与RTTS和新的RTT样本之差有关。RFC6289建议这样计算RTTD。当第一次测量时,RTTD值取为测量到的RTT样本值的一半。在以后的测量中,测使用下式计算加权平均的RTTD:

这里β是个小于1的系数,它的推荐值是1/4,即0.25

上面所说的往返时间的测量,实现起来相当复杂。试看下面的例子。

如图5-19所示,发送出一个报文段,设定的重传时间到了,还没有收到确认,于是重传报文段。经过一段时间后,收到了确认报文段。现在的问题是:如何判定此确认报文段是对方发送的报文段的确认,还是对后来重传的报文段的确认?由于重传的报文段和原来的报文段完全一致,因此源主机在收到确认后,就无法做出正确的判断,而正确的判断对确定加权平均RTTs的值关系很大。

若收到的确认是对重传报文段的确认,但却被源主机当成是对原来的报文段的确认,则这样计算出的RTTs和超时重传时间RTO就会偏大。若后面再发送的报文段又是经过重传后才收到确认报文段,则按此方法得出的超时重传时间RTO就越来越长。

同样,若收到的确认是对原来的报文段的确认,但被当成是对重传报文段的确认,则由此计算出的RTTs和RTO都会偏小。这就必然导致报文段过多地重传。可能使RTO越来越短。

根据以上所述,Karn提出了一个算法:在计算加权平均RTTs时,只要报文段重传了,就不采用其往返时间样本。这样得出地加权平均RTTs和RTO就较准确。

但是,这又引起新的问题。设想这样地情况:报文段的时延突然增大了很多。因此在原来得出的重传时间内不会受到确认报文段,于是就重传报文段。但根据Karn算法,不考虑重传报文的往返时间样本。这样,超时重传时间就无法更新。

因此要对Karn算法进行修正。方法是:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是取新的重传时间为旧的重传时间的2倍。当不再发生报文段的重传时,才根据上面给出的式(5-5)计算超时重传时间。这样较为合理。

总之,Karn算法能够使运输层区分有效的和无效的往返时间样本,改进往返时间的估测。


3. 选择确认SAK

还有一个问题没有讨论。就是若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不传送已经正确到达接收方的数据?答案是可以的。选择确认(Selective ACK)[RFC 2018, 建议标准]就是一种可行的处理方法。

用一个例子来说明选择确认的工作原理。TCP的接收方在接收对方发送过来的数据字节流的序号不连续,结果就形成了一些不连续的字节块(如图5-20所示)。可以看出,序号1~1000收到了,但序号1001~1500没有收到。接下来的字节流又收到了,可是缺少了3001~3500。再后面从序号4501起又没有收到。也就是说,接收方收到了和前面的字节流不连续的两个字节块。如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。

上图可看出,和前后字节不连续地每一个字节快都有两个边界:左边界和右边界,因此在图中用四个指针标记这些边界。注意,第一个字节块地左边界L1 = 1501,但右边界R1 = 3001而不是3000。这就是说,左边界指出字节块地第一个字节的序号,但右边界减1才是字节块的最后一个序号。同理,第二个字节块的左边界L2 = 3501,而右边界R2 = 4501.

TCP的首部没有哪个字段能够提供上述这些字节块的边界信息。RFC2018规定,如果使用选择呢SACK,那么在建立TCP连接时,就要在TCP首部的选项中加上“允许SACK”的选项,而双方必须都事先商量好。如果使用选择确认,那么原来首部中的‘确认号字段“的用法仍然不变。只是以后在TCP报文段的首部中都增加了SACK选项,以便报告收到的不连续的字节块的边界。由于首部选项的长度最多只有40字节,而指明一个边界就要用掉4字节(因为序号有32位,需要使用4个字节表示),因此在选项中最多只能指明4个字节块的边界信息。这是因为4个字节块共有8个边界,因而需要用32个字节来描述。另外还需要两个字节,一个字节用来指明是SACK选项,另一个字节指明这个选项要占用多少字节。如果报告5个字节块的边界信息,那么至少需要42个字节。这就超过了选项长度40字节的上限。互联网建议标准RFC2018还对报告这些边界信息的格式都做出了非常明确的规定。

然而,SACK文档并没有指明发送方应当怎样响应SACK。因此大多数的实现还是重传所有未被确认的数据块。


七、TCP的流量控制

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

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

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

下图的例子说明如何利用滑动窗口进制进行流量控制。

设A向B发送数据。在连接建立时,B告诉了A:“我们接收窗口rwnd=400.”(rwnd标识receiver window)。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。注意,TCP的窗口单位是字节,不是报文段。TCP连接建立

2. TCP的传输效率

八、TCP的拥塞控制

1. 拥塞控制的一般原理

2. TCP的拥塞控制

3. 主动队列管理AQM

九、TCP的运输连接管理

1. TCP的连接建立

2. TCP的连接释放

3. TCP的优先状态机

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2302225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MATLAB图像处理:图像分割方法

图像分割将图像划分为具有特定意义的子区域&#xff0c;是目标检测、医学影像分析、自动驾驶等领域的核心预处理步骤。本文讲解阈值分割、边缘检测、区域生长、聚类分割、基于图的方法等经典与前沿技术&#xff0c;提供MATLAB代码实现。 目录 1. 图像分割基础 2. 经典分割方…

【VSCode】MicroPython环境配置

【VSCode】MicroPython环境配置 RT-Thread MicroPython 插件安装MicroPython 库文件配置结束语 RT-Thread MicroPython 插件安装 在 VSCode 拓展中搜索 “RT-Thread MicroPython” 并安装&#xff0c;详细配置步骤&#xff08;修改 VSCode 默认终端、MicroPython 代码补全&…

【python】网页批量转PDF

安装wkhtmltopdf 网站&#xff1a;wkhtmltopdf wkhtmltopdf http://www.baidu.com/ D:website1.pdf 安装pdfkit库 pip install pdfkit 批量转换代码 import os import pdfkit path_wkthmltopdf rE:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe config pdfkit.configu…

基于Flask的租房信息可视化系统的设计与实现

【Flask】基于Flask的租房信息可视化系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 随着互联网的快速发展&#xff0c;租房市场日益繁荣&#xff0c;信息量急剧增加&#xff…

Scrapy安装,创建Scrapy项目,启动Scrapy爬虫

Scrapy安装&#xff0c;创建Scrapy项目&#xff0c;启动Scrapy爬虫 1. 安装 Python2. 安装 Scrapy3. 验证安装4. 创建 Scrapy 项目5. 启动爬虫5.1 示例 总结 Scrapy 的安装方式比较简单&#xff0c;下面是基于 Python 环境的安装流程&#xff1a; 1. 安装 Python 首先&#x…

C++项目:高并发内存池_上

目录 1. 项目介绍 2. 内存池概念 2.1 池化技术 2.2 内存池和内存碎片 2.3 细看malloc 3. 定长内存池的实现 ObjectPool.hpp 4. 高并发内存池框架 5. thread cache测试 5.1 thread cache框架 5.2 ConcurrentAlloc.hpp 6. central cache测试 6.1 central cache框架 …

手机控制电脑远程关机

远程看看软件兼容iOS和Android设备&#xff0c;该软件除了能通过电脑远程关闭另一台电脑外&#xff0c;您还可以通过它在手机上远程关闭公司的电脑。您可以按照以下步骤进行操作以实现电脑远程关机&#xff1a; 步骤1.在手机应用商店搜索“远程看看”进行软件安装&#xff0c;…

IO模型与NIO基础--NIO网络传输选择器--字符编码

放进NIO体系进行网络编程的工作流程&#xff1a; Selector的创建 通过调用Selector.open()方法创建一个Selector&#xff0c;如下&#xff1a; Selector selector Selector.open(); 向Selector注册通道 通过Channel.register()方法来实现&#xff0c; 注意&#xff1a;Chan…

【亚马逊开发者账号02】终审问题SA+review_Pre-review+Doc.xlsx

1.终审问题 你好感谢您在此过程中的回复和协作。所有想要构建具有受限 SP-API 角色的公开可用应用程序的开发人员都必须与我们的解决方案架构师团队一起完成架构审核。 这将需要详细说明应用程序的数据流、个人身份信息 &#xff08;PII&#xff09; 的数据保护控制&#xff0…

c++标准io与线程,互斥锁

封装一个 File 类&#xff0c; 用有私有成员 File* fp 实现以下功能 File f "文件名" 要求打开该文件 f.write(string str) 要求将str数据写入文件中 string str f.read(int size) 从文件中读取最多size个字节&#xff0c; 并将读取到的数据返回 析构函数 #…

在高流量下保持WordPress网站的稳定和高效运行

随着流量的不断增加&#xff0c;网站的稳定和高效运行变得越来越重要&#xff0c;特别是使用WordPress搭建的网站。流量过高时&#xff0c;网站加载可能会变慢&#xff0c;甚至崩溃&#xff0c;直接影响用户体验和网站正常运营。因此&#xff0c;我们需要采取一些有效的措施&am…

Cython学习笔记1:利用Cython加速Python运行速度

Cython学习笔记1&#xff1a;利用Cython加速Python运行速度 CythonCython 的核心特点&#xff1a;利用Cython加速Python运行速度1. Cython加速Python运行速度原理2. 不使用Cython3. 使用Cython加速&#xff08;1&#xff09;使用pip安装 cython 和 setuptools 库&#xff08;2&…

web的分离不分离:前后端分离与不分离全面分析

让我们一起走向未来 &#x1f393;作者简介&#xff1a;全栈领域优质创作者 &#x1f310;个人主页&#xff1a;百锦再新空间代码工作室 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[1504566…

记录一个ES分词器不生效的解决过程

问题背景 商城项目,其中商品查询检索使用的是ES, 但存在某些商品查询不到的问题 例如:某商品名包含AA_BBB这样的关键词,但是搜索"AA"不能查询到该商品,但是将商品名修改为AA BBB后就能查询到了. 怀疑是分词的问题,但看代码,在创建ES索引时在对应字段上也定义了分词器…

高性能内存对象缓存Memcached详细实验操作

目录 前提准备&#xff1a; cache1&#xff0c;2&#xff1a; 客户端cache-api&#xff08;一定得是LAMP环境&#xff09; memcache实现主主复制以及高可用(基于以上完成) cache1,2: memcachekeepalived(基于以上完成) cache1,2: 前提准备&#xff1a; 1. 准备三台cent…

css之display:grid布局改块级元素布局

1.问题&#xff1a; div是块级元素&#xff0c;一个div元素占一行&#xff0c;但是&#xff0c;今天测试样式时&#xff0c;总是会有两个div并占一行&#xff0c;很困惑&#xff0c;结果发现是app这个样式 在main.css里 #app样式布局在main.ts里被应用 2.原因以及样式分析 im…

推荐一个github star45k+进阶的java项目及知识的网站

mall是github上star 45k的一个java项目 mall项目是一套电商系统&#xff0c;包括前台商城系统及后台管理系统&#xff0c;基于SpringBootMyBatis实现&#xff0c;采用Docker容器化部署。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心…

第2章 深入理解Thread构造函数

Thread的构造函数。 2.1 线程的命名 在构造一个Thread时可以为其命名。 2.1.1 线程的默认命名 下面构造函数中&#xff0c;并没有为线程命名。 Thread() Thread(Runnable target) Thread(ThreadGroup group, Runnable target)打开源码会看到 public Thread(Runnable targe…

node 使用 Redis 缓存

缓存是什么&#xff1f; 高并发下&#xff0c;一个项目最先出问题的&#xff0c;并不是程序本身&#xff0c;而是数据库最先承受不住。 在数据库上我们可以做很多优化&#xff0c;例如优化 SQL 语句&#xff0c;优化索引&#xff0c;如果数据量大了&#xff0c;还可以分库、分表…

PMBOK第7版整体架构全面详解

1. 引言 7月1日对于项目管理从业者和研究者而言&#xff0c;是个非凡意义的一个时间&#xff0c;这一天&#xff0c;翘首以待的《 项 目管理知识体系指南 》&#xff08;PMBOK&#xff09;第七版终于发布了。 总体而言&#xff0c;PMBOK第七版集百家之所长&#xff0c;成一…