计算机网络原理(三):运输层

  • 运输层服务
  • 多路复用与多路分解
  • 无连接运输:UDP
  • 可靠数据传输原理
  • 面向连接的运输:TCP
  • 拥塞控制原理及TCP拥塞控制

 一、运输层服务

1.1运输层服务

运 输层协议为运行在不同主机上的应用进程之间提供了逻辑通信(logic communica-tion)功能,从应用层的角度看,通过逻辑通信,运行不同进程的主机好像直接相连一样。而实际上这些主机也许位于地球的两侧,通过 很多路由器及多种不同类型的链路相连。应用进程使用运输层提供逻辑通信功能彼此发送报文,而无须考虑承载这些报文的物理基础设的细节。

运输层协议是在端系统中而不是在路由器中实现,在发送端,运输层将从发送应用程序进程接收到的报文转换成运输分组,用因特网术语来讲该分组称为运输层报文段(sement)。

运输层服务的实现方法:

1.将应用报文划分为较小的块,并为每块加上一个运输层首部以生成运输层报文段。
2.在发送端系统中,运输层将这些报文段传递给网络层。
3.网络层将其封装成网络层分组(即数据报)并向目的地发送。

需要注意的是网络路由器仅作用于该数据报的网络层字段,即它们不检查封装在该数据报的运输层报文段的字段。在接收端,网络层从数据报中提取运输层报文段,并将该报文段向上交给运输层。运输层则处理接收到的报文段,使该报文段中的数据为接收应用进程使用。

网络应用程序可以使用多种的运输层协议,例如因特网有两种协议:TCP和UDP。每种协议都能为调用的应用程序提供一组不同的运输层服务。

3.2运输层与网络层的关系

运输层位于网络层之上,网络层提供了主机之间的逻辑通信,而运输层为运行在不同主机上的进程之间提供了逻辑通信。用现实中寄信来类比网络通信:

1.应用层报文相当于信封上的字符;
2.寄信人和收信人相当于进程;
3.主机(又称为端系统)相当于各自的家庭;
4.运输层协议相当于是具体帮寄信人的信件投进邮筒的人和从邮筒收取信件交给收信人的人,这或许是你的家人。
(如果是你自己去投递和取件就相当于充当了网络通信环节中的两个角色,实际上在网络通信中这是有两个完全不同的组件完成的)
5.网络层协议相当于邮政局的服务,他负责将信件从寄信的邮筒取出,并通过中间系列的货运环节最终送到收信人的邮筒。

网络通信和现实中寄信也一样,我们的收信时长取决于邮局的服务,这中间同样也可能存在信件丢失损坏的可能。为了加快信件的投递速度,邮局会使用各种较同工具比如卡车、飞机,减少中间的中转环节和选择更快的路径等;在网络通信中,网络层不断的改上网络基础设施、使用的不同的通信链路,选择不同的路由转发来加快数据的传输速率。如果在现实中信件丢失或者顺坏,就可能需要我们重新邮寄;在网络通信中一样,像TCP就会通过重传等方式来处理报文丢失的问题。更多具体的网络通信运输层细节问题,在后面的小节详细介绍。

3.3因特网运输层概述

因特网为应用层提供了两种截然不同的运输层协议:UDP和TCP。

UDP(用户数据报协议):给调用它的应用程序提供了一种不可靠、无连接的服务。

TCP(传输控制协议):给调用它的应用程序提供一种可靠、面向连接的服务。

一般为了简化术语,通常将运输层分组称为报文段(segment),因特网文献RFC中也将TCP的运输层分组称为报文段,将UDP的分组称为数据报。而这类因特网文献也将网络层分组称为数据报,为了不混淆概念我们通常情况下将TCP和UDP的分组称为报文段,将数据报保留给网络层分组。

在对UDP和TCP进行简要介绍之前,简单的介绍一下因特网的网络层是有必要的,因特网网络层协议有一个名字叫做IP,即网际协议。IP为主机之间提供逻辑通信,IP的服务模型是尽力而为交付服务(best-effort delivery serv-ice)。这就是前面的寄信例子中邮局可能丢失和损坏信件一样,IP它不确保报文段的交付,其中就包括不确保按时交付和不确保数据完整交付,因此IP也被称为不可靠服务。

UDP和TCP所提供的服务模型最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。将主机交付扩展到进程交付被称为运输层的多路复用(transport-layer multiplexing)与多路分解(demultiplexing),这将在下一节详细的解析。

UDP和TCP还可以通过其报文段首部中包括差错检查字段而提供完整性检查,进程到进程的数据交付和差错检查是两种最低限度的运输层服务,也是UDP所能提供的仅有的两种服务。与IP一样,UDP也是一种不可靠的服务,即不能保证一个进程所发送的数据能够完整无缺地到达目的进程,这些具体的内容在后面的无连接运输UDP中详细介绍。

TCP为应用程序提供了几种附加服务,首先它提供了可靠数据传输(relia-ble data transfer)。通过使用流量控制、序号、确认和定时器,TCP确保正确的、按序地将数据从发送进程交付给接收进程。TCP还提供拥塞控制(con-gestion control),TCP拥塞控制是防止一条TCP连接用过多的流量来淹没通信主机之间的链路和交换设备,TCP力求为每个通过一条拥塞网络链路的连接平等地共享网络链路带宽,这个可以通过调节TCP连接的发送端发送进网络的流量速率来做到。

 二、多路复用与多路分解

讨论运输层的多路复用和多路分解,也就是将由网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的服务。虽然这里讨论的是运输层服务,然而多路复用和多路分解服务实所有计算机网络都需要的。

一个进程有一个或多个套接字(socket),套接字相当于从网络向进程传递数据和从进程向网络传递数据的门户,因此在主机中的运输层实际上并没有直接将数据交付给进程,而是将数据交付给了一个中间的套接字。由于在任一时刻,在接收主机上可能不止一个套接字,所以每个套接字都有唯一的标识符,标识符的格式取决于它是UDP还是TCP套接字。

多路分解(demultiplexing):考虑接收主机怎样将一个到达的运输层报文段定向到适当的套接字,每个运输层报文段中具有几个字段,在接收端,运输层检查这些字段,标识出接收套接字,进而将报文段定向到该套接字。将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。

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

2.1多路复用的与多路分解的的工作原理

运输层多路复用的基本要求包括两个:套接字有唯一标识符、每个报文段有特殊字段来指示该报文段所交付到的套接字。

如上图中的源端口号字段(source port number field)和目的端口号(destination port number field)字段,在UDP和TCP报文中还有其他的一些字段。端口号是一个16比特的数,其大小在0~65535之间,0~1023范围的端口号称为周知端口号(well-known port number)是受限制的,比如保留给HTTP使用的80端口号和FTP使用的21端口号之类的应用层协议来使用,所谓端口号在操作系统实际上就是进程的唯一标识符PID,这实际上是属于操作系统的相关内容,不过有一些关联性仅做简单介绍。

现在了解运输层是怎样实现分解服务了,实际上就是在主机上的每个套接字能够分配一个端口号,当报文到达主机时,运输层检查报文中的目的端口号,并将其定向到相应的套接字。简单来说就是一个报文段的报文上有应用程序的客户端和服务端两个进程的唯一标识,然后运输层就知道这个报文段要就给那个套接字了。

2.2无连接的多路复用与多路分解

在上一篇博客计算机网络原理(二):应用层中的套接字编程中介绍过,通过 server.bind() 将一个服务实例生成的套接字分配到指定的主机端口上,当然如果应用程序使用的是周知端口号必须分配相应的端口号,应用程序的客户端可以让运输层自动分配端口号也可以指定端口号。但是需要注意的是在UDP报文段中只有源端口号和目的端口号,网络层将该报文段封装到一个IP数据报中,也就是说在UDP的运输层上它并不维护客户端的IP地址,只是在套接字发送报文时指定目的IP地址,这个IP地址只是提交给下一层网络层使用,然后接受方套接字在接受时会从网络层拿到发送端的IP地址交给应用程,它自身并不会维护这个源IP地址,在UDP的套接字上它只有自己的IP地址和端口号,而它传输报文段都是在同一个套接字上,从计算机网络原理(二):应用层中的套接字编程中可以看到它实际上是在同一个套接字上发送消息(即报文段)。

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

而源端口号用作返回地址,当服务接收到客户的报文段以后取出其中的IP地址和端口号,就可以通过这IP地址和端口号向客户端回发一个报文。

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

TCP套接字是一个四元组:源IP地址、源端口号、目的IP地址、目的端口号来标识,在上一篇博客计算机网络原理(二):应用层中的套接字编程中介绍过,TCP的服务端通过服务实例的 server.listen() 生成的并不是一个套接字而是一个服务对象,这个服务对象监听服务进程,客户端通过 net.createConnection() 发起连接时生成一个套接字实例,服务实例实例的connection事件 server.on('connection', (socker) =>{}) 连接监听到一个请求到达时生成一个服务端的套接字实例,这两个套接字实例的源IP地址、源端口号是生成它们的服务和客户实例的主机IP地址、端口号;目的IP地址、目的端口号是彼此的源IP地址、源端口号。所以TCP的报文结构可以如下表示:

TCP套接字为什么是一个四元组,这需要从TCP提供的服务角度来说,首先TCP服务需要同时维护多个套接字连接,而UDP服务只是负责接收客户端的报文,所以UDP服务只需要一个套接字就可以完成它的服务。而TCP需要保持对客户套字节后续的整个连接通信过程,套接字在这个过程中通过向上层的应用程序提供通信接口,上层应用并不需要关注套接字的连接细节,应用层只需要通过接口向套接字读写数据即可,不像UDP在应用层如果需要向客户回发数据需要自己写入目的客户的IP及端口号。这些描述可能会显得有些抽象,在后续的运输层通信过程解析中就很容易理解为什么会这样了。

 三、无连接运输:UDP

UDP从应用程序进程得到数据,附加上用于多路复用和多路分解的源和目的端口号字段,以及两个其他的小字段,然后将形成的报文段交给网络层。网络层将该运输层报文封装到一个IP数据报中,然后尽力而为地尝试将此报文段交付给接收主机。如果报文段到达接收主机,UDP使用目的端口号将报文段中的数据交付给相应的应用进程。值得注意的是,使用UDP时,在发送报文段之前,发送方和接受方的运输层实体之间没有握手。所以,UDP被称为是无连接的。

还记得在计算机网络原理(二):应用层中介绍的DNS吗?DNS应用想要进行一次查询,它构造一个DNS报文并将其交给UDP,主机端的UDP将此报文添加首部字段,然后将形成的报文段交给网络层,网络层将此UDP报文段封装进一个IP数据报中,然后将其发送给一个名字服务器。在查询主机中DNS应用程序则等待对查询的响应,如果在一定时间内没有得到响应,要么试图向另一个名字服务器发送该查询,要么通知调用的应用程序“它不能获得响应”。

3.1为什么要使用UDP

为什么在开发应用时宁愿选择UDP的不可靠传输,而不选择TCP的可靠传输,大概原因有以下几点:

 1.可以更精细的控制发送数据:采用UDP时,只要应用进程将数据传递给UDP,UDP就会将此数据打包进UDP报文段并立即将其传递给网络层。而TCP除了有一个拥塞控制机制,可能会在传递报文段时会产生较大的时延。

2.无须建立连接:在建立连接这个问题上,其实是TCP的一个服务功能,后续在TCP的相关内容中介绍,这里只需要知道UDP不会引入建立连接的时延。

3.无连接状态:跟上一条一样,这个问题也是TCP的连接机制形成的,端系统中维护连接状态,在此连接状态上包括接收发送缓存、拥塞控制参数以及序号和确认号的参数。而UDP不需要维护连接,也就不需要跟踪这些参数。因此UDP相比TCP在同样的系统资源情况下,能支持更多的活跃用户。

4.分组首部开销小:每个TCP报文段都有20个字节的首部开销,而UDP仅有8字节的开销。

综合上述的分析如果能容忍一些数据丢失的风险,考虑低时延的传输需求的话应用可以使用UDP。比如很多重要的应用都是运行在UDP上而不是TCP上,除了前面提到的DNS服务,还有承载网络管理数据的SNMP,因为网络管理应用程序通常必须在该网络处于重压状态时运行,而这时候可靠的、拥塞控制的数据传送很难实现,而DNS运行在UDP上可以避免创建连接的时延。

现在很多UDP和TCP都用于多媒体应用,如因特网电话、时实视频会议、流式存储音视频,这些应用都能容忍数据丢失,因此可靠数据传输对这些应用并不重要,由于这些原因,多媒体应用通常运行在DUP上。但当分组丢包率低时,并且为了安全原因,某些机构阻塞UDP流量,对于流式媒体传输来说,TCP变得越来越有吸引力。

3.2UDP报文结构与检验和

UDP报文有四个字段,分别是:源端口号、目的端口号、长度、检验和。在前面已经介绍了源端口号和目的端口号的作用,下面来关注另外两个字段。

UDP报文的长度字段:指示了在UDP报文段的字节数,包含首部和数据。

UDP报文的检验和字段:用来检查该报文段是否出现差错。

检验和的检查差错实现原理:UDP检验和提供了差错检测功能,检验和用于确定UDP报文段从源到达目的地移动时,其中的比特是否发生改变(例如,由于链路中的噪声干扰或者存储在路由器中时引入问题)。发送方的UDP对报文段中的所有16比特的和进行反码运算,求和时遇到的任何溢出都被回卷。得到的结果被放在UDP报文段的检验和字段。

假设下面3个比特的字:
0110011001100000
0101010101010101
1000111100001100
这些16比特的前两个之和是:
0110011001100000
0101010101010101
1011101110110101
再将上面的和与第三个字相加,得出:
1011101110110101
1000111100001100
0100101011000010

需要注意的是最后一次加法有溢出,它被回卷。反码运算就是将所有的0换成1,所有的1换成0,得到的结果就是检验和。如果该分组中没有引入差错,则显然在接受方该处和将是1111111111111111,如果这些比特之一是0,那么就可以得出该分组中已经出现了差错。

在接受方的UDP收到报文段以后,对报文检查计算出校验和与检验和字段的内容是否相等,如果不相等就表示分组出现差错,但UDP对错误恢复无能为力,UDP的实现就是将出现差错的报文段丢弃。

 四、可靠数据传输原理

考虑可靠数据传输的问题,不仅仅是在运输层,也会出现在链路层。可靠数据传输的框架,为上层提供的服务抽象是:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏或丢失,而且所有数据都按照其发送顺序进行交付。这恰好就是TCP向调用它的因特网应用所提供的服务模型。

TCP是在不可靠的(IP)端到端网络层之上实现的可靠数据传输协议,更一般的情况是,两个可靠通信端点的下层可能是由一条物理链路组成或是由一个全球互联网络组成。就可靠数据传输目的而言,可以将较低层直接视为不可靠的点对点信道。底层信道损坏比特或丢失整个分组时,需要什么样的协议机制,这贯穿我们讨论始终假设分组将以它们发送的次序进行交付,某些分组可能会丢失,这就是说,底层信道不会对分组重排序。

上图简单的模拟传输协议接口。(rdt表示可靠数据传输协议,_send指示rdt的发送端正在表调用。udt表示不可靠的数据传输)

rdt_send()函数表示可以调用传输协议的发送方;
udt_send()表示发送端和接收端发送分组给对方;
deliver_data()表示rdt向叫高层交付数据的方法;
rdt_rcv()表示rdt从底层信道接收一个分组;

4.1构造可靠数据传输协议

现在一步步地研究一系列协议,它们一个比一个复杂,最后得到一个无错、可靠的数据传输协议。首先,考虑最简单的情况,即底层信道是完全可靠的,将此协议定义为rdt1.0。

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

上图表示发送方和接收方的有限状态机(Finite- State Machine, FSM),上图(左)中的FSM定义了发送方的操作,上图(右)中FSM定义了接收方的操作。

上面的流程图描述了rdt1.0发送端的处理过程。

上面的流程图描述了rdt1.0接收端的处理过程。

虽然在rdt1.0中是基于一个理想中完全可靠信道来实现的数据传输,但是也描述了运输层的基本功能就是负责网络层与应用层的数据传输,我们将这个运输层协议先成为简单协议。在简单协议中,一个单元数据与一个分组没有差别,而且所有分组是从发送方流向接收方,有完全可靠信道,接收不需要任何反馈信息给发送方。注意,我们也假定了接收方与发送方的速率一样快。

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

底层信道实际的模型是分组中的比特可能受损,分组的传输、传播或缓存的过程中,这些比特差错通常出现在网络物理部件中。在讨论比特差错信道的可靠数据传输时,我们假设所有发送的分组还是按照顺序接收的。相较完全可靠信道只有增加比特差错这一种情况。

从理论上来讲,只需要将比特差错的分组识别出来,然后将差错信息反馈给发送方,让发送方将差错分组重新发送一次。在计算机网络中,基于这样重传机制的可靠传输一些被称为自动重传请求协议(ARQ)。ARQ协议中还需要另外三种协议功能来处理存在比特差错的情况:

差错检测:基于检验和的比特差错识别。(在《计算机网络》第五章有详细的差错检测和纠错技术分析)
接收方反馈:当接收到正常分组时反馈“肯定确认”(ACK),当接收到比特差错分组时反馈“否定确认”(NAK),用0表示NAK,1表示ACK。
重传:接收方收到有比特差错的分组,发送方将重新传递该分组文。

理论上来说,好像有ARQ协议就可以实现比特差错信道的可靠数据传输。发送方每传输一个分组给接收方,等待接收方反馈回信息,如果返回的是ACK就继续传输下一个分组,如果返回的是NAK,就重新传输上一个分组,这种行为又被称为停等协议。到这里,好像rdt2.0就可以实现经具有比特差错信道的可靠数据传输了,别高兴太早,仔细看看反馈信息是什么?它本身也是被信道传输的比特,怎么才能保证反馈信息的比特不出错呢?这是不可能的,因为物理传输受损是必然的,那要怎么解决呢?

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

第一种方法(重复应答):当发送方收到的反馈信息比特受损无法识别时,发送方请求接收方再重复一次反馈。这种可能性有一个非常极端的情况就是如果重复发送的反馈还是丢失比特呢?又或者是发送方发送重复反馈请求时丢失比特呢?这就陷入了死循环。
第二种方法:增加足够的检验和比特,使发送方不仅可以检测差错,还可以修复差错。对于会产生差错但不丢失分组的信道,这就可以直接解决问题。
第三种方法:当发送方收到含糊不清的ACK或者NAK分组时,只需要重传当前数据分组即可。然而,这种方法在发送方到接收方的信道中引入了冗余分组。冗余分组的根本困难在于接收方不知道它上次发送的ACK或NAK是否被正确收到,因此它无法事先知道接收到分组是新的还是一次重复重传。

解决这个新问题(第三种方法中的无法确认分组问题)的方法非常简单,在数据分组中添加一个新字段,让发送方对其数据分组编号,即将发送数据分组的序号放到该字段。重传的分组序号与最近接收到的分组序号相同,新的分组序号会变化(使用模2“前向”移动:模2运算)。目前我们假定的是信道不会丢失分组,所以ACK/NAK分组本身不需要指明他们要确认的分组序号。发送方接收到ACK/NAK分组是为响应最近发送的数据分组而生成的。rdt2.1反映出目前正在发送的分组或希望接收的分组的序号是0还是1。rdt2.1使用了从接收方收到的肯定确认和否定确认。当接收方收到失序分组时,发送肯定确认。如果收到受损分组,则发送否定确认。如果不发送NAK,而是对正确接收到的分组发送一个ACK,那么也能得到与NAK一样的效果。发送方收到对同一个分组的两个ACK(接收到了冗余ACK)后,就可以知道接收方没有正确接收被确认两次的分组后面的分组。这也产生了协议rdt2.2。rdt2.2是在有比特差错信道上实现的一个无NAK的可靠传输协议,此时ACK报文就需明确所确认的分组序号。(这一段的具体实现不太明白《计算机网络》P141~143)。

 

 4.1.3经具有比特差错的丢包信道可靠数据传输:rdt:3.0

现在除了比特损坏外,底层信道还会丢包,这样的情况并不罕见。所以协议必须关注两个问题:怎么检测丢包以及发生丢包后该做些什么?

在rdt2.2中已经使用的技术有检验和、序号、ACK分组、和重传等。在这个基础上考虑结局丢包问题,丢包所造成的问题就是接收方无法响应,不会反馈信息给发送方,这里就有了一个突破口,就是延时时间,假设已知在不丢包的情况下接收方的反馈时间是n,那就可以采用倒计数定时器等待时间n后再次发送上次的分组,这同样会产生冗余数据分组,而这样的问题再rdt2.2中已经得到解决,所以丢包问题也就解决了。

 4.2流水线可靠数据传输协议

在rdt3.0中的以及2.2中都采用了停等协议,对信道的利用率非常的低(案例见《计算机网络》P144~146)。解决这个问题的简单方案就是:流水线可靠数据传输协议。流水线可靠数据传输协议采用发送多个分组(同样序号的一个分组发送多个)的方式来实现。

多个分组的运输原理是基于信道实际容量和单个分组传输的使用比率来实现的,也就是假设使用停等协议传输方式只是用了信道传输容量的33%,那多分组传输就可以一次连续传输三个相同序列的分组,这样信道利用率就提高了3被,也就提高了分组的传输成功率。这样产生的连锁反应就是成功率提升降低了重传,降低了重传就降低了反馈比特损坏和丢失率,整体性能就会提高很多。

流水线可靠数据传输协议除了多分组传输,当然就是采用流水线的传输方式连续传输,那什么是流水线传输呢?为什么要使用流水线传输呢?

采用多分组传输实现了非常贴近完全可靠传输,就不必要采用停等应答的低效方式来解决比特损坏和分组丢失,而是采用回退N步和选择重传的方式来解决比特损坏和分组丢失问题。在解析回退N步和选择重传的具体技术之前,先来看看流水线完全可靠协议的基本实现逻辑:

从上面的流水线可靠传输协议示图中可以看到每一个分组都会被发送多次,这是流水线可靠传输协议的核心,只要多个分组中有一个没有损坏就可以实现了可靠传输。但是,也可能会出现极端的情况就是整组损坏的情况,在示图中我标识了重传反馈,其实不正确,只是为了区分以下整组丢失、反馈比特损坏导致的超时,其实质上都是使用选择重传来解决。在解析选择重传之前需要先来说明以下回退N步(GBN)协议的原理,这是因为流水线可靠传输协议带来的下列影响造成的:

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

4.2.1回退N步

在回退N步(GBN)协议中,允许发送方发送多个分组而不需要等待确认,但是也受限于在流水线中未确认的分组数不能超过某个最大允许数N。在流水线可靠协议示图中可以看到GBN协议的序号范围。如果将基号(base)定义为最早的未确认分组序号,将下一个序号(nextseqnum)定义为最小的未使用序号,则可以将序号范围分为四段:在[0,base-1]段内的序号对应于已发送并被确认的分组;[base,nextseqnum-1]段内对应已发送但未被确认的分组;[nextseqnum,base+N-1]段内的序号用于可以被立即发送的分组。如果有数据来自上层的话,最后大于base+N的序号不能被使用,知道当前流水线中未被确认的分组已得到确认。

随着协议进行,该窗口序号空间向前滑动,因此N常被称为窗口长度,GBN协议也常被成为滑动窗口协议。在此我们限定的窗口长度N,而不是让分组数为无限大是有两个原因①流量控制②TCP拥塞控制。如果分组序号字段的比特数为k,那么序号范围就是[0,2^k-1],在一个有限的序号范围内,所有涉及序号的运算必须使用模2^k运算。

GBN发送方必须响应三种类型的事件:

上层的调用:当上层窗口调用发送时,发送方首先检查发送窗口是否已满(即是否有N个已发送但未被确认)。如果窗口未满,则产生一个分组并将其发送,并更行响应变量。如果窗口已满,发送方只需要将数据返回给上层,隐式指示上层该窗口已满。然后上层可能会等一会儿再试。实际实现中,发送方可能会缓存这些数据,或者使用同步机制允许上层在仅当窗口不满时才调用发送方法。
收到一个ACK:在GBN协议中,对序号为n的分组的确认采用积累确认的方式,表明接收方以正确接收到序号n的以前且包括n在内的所有分组。
超时事件:回退N步来源于出现丢失和延时过长时的处理行为,就像停等协议中那样,如果接收方的反馈时间超出发送方的定时器时间(分组丢失损坏,反馈比特损坏,网络延时长),发送方就会重新发送已发送当未被确认的分组,这就是回退N步的来源。

 下图表示了GBN的运行模式:

 

GBN协议中综合了可靠数据传输协议构件的所有技术,这些技术包括使用序号、积累确认、检验和超时/重传操作。采用GBN这种协议看似好像很合理很实用,但是却有一个非常大的闭端,随着协议的运行,重传的分组就会积累的越多,看到上面的示图就一目了然,后面的重传分组和正常分组都一样多了,而且随着时间推移,重传会更加频繁,所以后面就有了选择重传来解决这个问题。

4.2.2选择重传

GBN也存在着一些性能问题,单个的分组出现差错就会引起GBN重传大量不必要重传的分组。在信道差错率很高时,流水线可能会被不必要重传的分组所充斥。可操作此处SR Java小程序查看SR运作流程。

SR协议通过让发送方仅重传那些它怀疑在接受方出错(丢失或受损)的分组而避免不必要的重传。下图(来自《计算机网络 自定向下方法》)显示了SR协议中发送方和接收方的序号范围。在SR协议中发送方和接收方所采取得动作可见下文描述。

SR发送方的事件与动作:

1.从上层接收到数据:从上层接收到数据后,SR发送方检查下一个可用于该分组的序号。如果该序号位于发送方的窗口内,则将数据打包并发送;否则就像再GBN中一样,要么将数据换组,要么返回给上层以便以后传输。
2.超时:定时器在此用来防止丢失分组。但是,SR中每个分组都要有自己的逻辑定时器。
3.收到ACK:如果收到ACK,倘若该分组序号在窗口内,SR发送方就将那个被确认的分组标记为已接收;如果该分组的序号等于send_base,则窗口基序号向前移动到具有最小序号的未被确认分组处;如果窗口移动了并且有序号落在窗口内的未发送分组,则发送这些分组。
SR接收方的事件与动作:SR接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存,知道所有丢失分组皆被收到为止,这时才将一批分组按序交付给上层。
4.序号在[rcv_base,rcv_base+N-1]内的分组被正确接收:在此情况下,收到的分组落在接收方的窗口内,一个选择ACK被回送给发送方;如果该分组以前没有被接收到过在,则缓存该分组;如果该分组的序号等于基序号,则该分组以及以前缓存的序号连续的分组交付给上层。然后,接收窗口按向前移动分组的编号向上交付这些分组。
5.序号在[rcv_base-N,rcv_base-1]内的分组被正确收到:在此情况下,必须产生一个ACK,即使该分组时接收方以前已确认的分组;要意识到这是非常重要的,假设接收方以前就接收了send_base分组,现在接收到重传的不回送ACK,那么发送方窗口就永远也不会向前滑动!
6.其他情况,忽略该分组:SR协议中发送方窗口和接收方窗口并不总是一致的。这会引出关于序号范围的一个问题。在有限的序号范围的实现里,如果SR接收方窗口太大并且发送方和接收方窗口间的不同步,便会造成发送方这边新分组序号与旧分组序号重复使用,导致无法辨析该序号代表的是一个新分组还是旧分组。SR的窗口长度应限制在小于等于序号空间大小的一半。

小结一下可靠数据传输机制中使用到的技术细节:检验和、定时器、序号、确认、否定确认、窗口/流水线。

 五、面向连接的运输:TCP

TCP被称为面向连接的协议,这是因为在一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立确保数据传输的参数。作为TCP连接建立的一部分,连接的双方都将初始化与TCP连接相关的许多TCP状态变量。这些变量将会是控制实际数据传输的重要逻辑变量,在介绍TCP实际运输逻辑之前,先来看一下TCP协议执行发送和接收的基本结构(缓存)示图:

看到上面这个图你一定会很惊讶,为什么与博客的第二部分“可靠数据传输协议”差别那么大?这是一个很关键的问题,在第二部分中说的可靠数据传输协议都是已分组传输为传输基本单元,但是需要注意的是,在TCP协议中传输的基本单元是用数据流来描述的,虽然本质上都是报文段,但是这两个描述差别将是TCP协议一切的起源(个人理解),从分组到数据流其主要的差别就是分组是从应用层直接调用运输层的接口实现,然后就直接被用来传输,在第二部分中出现的冗余是直接采用丢弃的方式来解决,甚至当分组出现错序的后面所有分组都被丢弃,这种粗暴的做法必然带来的就是增加了网络传输压力,浪费传输资源,结果就是传输效率低。而TCP协议采用了缓存的方式来解决这个问题,当然可靠数据传输的其他技术:检验和、定时器、序号、确认、否认确认、窗口/流水线、重传机制都被TCP作为基础而应用。实质上,TCP协议就是在写基础技术进一步优化,以达到可靠数据传输的最佳应用方案。

TCP提供的是全全双工服务(full-duplex-service),并且TCP连接也是点对点(piont-to-piont)的,这就说明TCP连接是单个发送方与单个接收方之间的连接。在一次发送操作中,从一个发送方将数据传给多个接收方,即“多播”操作对TCP来说是不可能的。

先就示图的基本逻辑来分析TCP从连接到数据传输的过程

1.客户首先发起一个特殊的TCP报文段,服务器另一端特殊的TCP报文段响应,最后,客户再用第三个特殊报文段作为响应。前两个报文段不承载“有效负荷”,也就是不包含应用层数据;而第三个报文段承载有效载荷。这个连接过程也叫做三次握手。
2.当数据被应用层通过套字节传递到运输层,TCP协议运行控制数据流,将数据引导到该链接的发送缓存里。发送缓存是在三次握手初期设置的缓存之一。
3.接这TCP从缓存中取出有最大限制长度的数据(MSS)加上报文首部合成报文发送给接收方。

最大限制长度的数据(MSS):通常根据最初确定的由本地发送主机发送的最大链路层帧长度(最大传输单元)来设置。设置最大传输单元主要依据以太网和PPP链路层的最大链路层帧决定。假设链路层最大链路帧是1500字节的MTU,而TCP报文首部长度通常是40字节,因此MSS的典型值为1460字节。

5.1TCP的报文结构

TCP报文段的结构与UDP一样,首部包括端口号和目标端口号,它被用于多路复用与多路分解或用来将数据送到上层的应用层。报文的数据部分就不解释了,就是上层应用层的报文,这里重点来关注TCP的首部结构。

数据偏移:TCP中数据的开始处距离TCP报文段的起始位置有多远 == TCP报文段的首部长度。表示长度以32位比特为单位,因此最大可以表示60字节(15*4)的首部。保留占位6以后使用。

标志字段含义
URG URG=1,用来指示报文段里存在着被发送端的上层实体置为“紧急”的数据。此时紧急指针有效
ACK 当ACK=1时,确认号字段有效,表示对已被成功接收的报文段的确认
PSH 当PSH=1时,指示接收方应立即将数据交付给上层,不用等接收缓存满了才交付
RST RST和下面的SYN、FIN用于TCP建立连接和释放连接。
RST用于①RST=1,TCP连接初出现严重差错,必须释放连接然后重新建立运输连接
②拒绝一个非法报文段或者拒绝打开一个连接
SYN 在TCP连接建立时用来同步序号
FIN FIN=1,释放TCP连接

序号与确认号:在开始介绍TCP时我就重点的描述了TCP的缓存和字节流,TCP把数据看成无结构的、有序的字节流。从TCP的序号上就可以看出这一点,序号是建立在字节流上,而不是建立在报文字段的序列之上,报文的序号是该报文首字节的字节流编号。假设数据流由一个包含500000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0,TCP将该数据流构建500个报文段,给第一个报文段分配序号0,第二个报文段分配序号1000,第三个报文段分配序号2000,以此类推。

将字节编号作为序号有什么好处呢?接着来看确认号,假设由A、B两个主机,A向B请求了一个报文段编号为0~1000的所有字节,但是在发送的过程中由于网络层和链路层的问题,A只接收到了0~536序号字段和900~1000的序号字段,这就出现了乱序的问题,通常解决这类问题有两个基本选择:1、接收方立即丢弃失序字段2、接收方保存失序字段,并等待缺少的字节以填补间隔。显然第二种方法更有效,而这第二种方法就需要确认号来完成这样精确的问题,TCP会先将1~536序号的字段添加到字节流中,然后将900~1000的序号字段暂时缓存,并将536序号作为确认好反馈给B,这时候B就知道了应该从发送方的缓存中取出537字节开始的字节流给接收方发送,直到发送发送到899字节流序号的报文,接收方将900~1000的字节流合并到字节流中,然后反馈确认号1000表示数据全部接收完毕。

5.2往返事件的估计与超时

在第二部分的可靠数据传输协议中介绍了超时/重传机制,同样,TCP协议也应用了同样的机制,但是在第二部分并没有就超时/重传的具体时间做任何讨论,TCP作为一个具体的可靠数据传输协议当然就必须对超时/重传的时间设定有一个明确的解决方案。RTT作为往返时间,超时设定值可能就要比RTT大。TCP的实现仅在某一时刻测量一次往返时间(SampleRTT),而且不会测量重传报文的往返时间([Kan 1987])。
由于路由器的拥塞和端系统负载的变化,这些报文段的SampleRTT值会随之波动,所以并非一个典型值。为了估计一个典型的RTT,TCP协议采用了对SampleRTT取平均值的办法。一旦获得一个新SampleRTT时,TCP就会根据下列公式来更新Esti-matedRTT:

  EstimatedRTT = (1 - a ) * EstimatedRTT + a * SampleRTT

EstimatedRTT的新值是由以前的Esti-matedRTT的值与SampleRTT新值加权组合而成的。在[RFC 6298]中给出的a参考值是a = 0.125(即:1/8),这时上面的公式变为:

  EstimatedRTT = 0.875 * EstimatedRTT + 0.125 * SampleRTT

通过加权平均是为了得到一个更能反映网络当前的拥塞情况,统计学的观点讲,这种平均被称为指数加权移动平均。在TCP中除了估算RTT外,测量RTT的变化也是有价值的。[RFC 6298]定义了RTT偏差DevRTT,用于估算SampleRTT一般会偏离EstimatedRTT的程度:

  DevRTT = (1 - b) * DevRTT + b * | SampleRTT - EstimatedRTT |

注意DevRTT是一个SampleRTT与EstimatedRTT之间差值的EWMA。如果SampleRTT值波动较小,那么DevRTT就会很小。反之,如果波动很大,那么DevRTT的值就会很大。b的推荐值为0.25。

设置和管理重传超时间隔

假设已经给出了EstimatedRTT值和DevRTT值,那么TCP超时间隔应该用什么值呢?超时间隔应该大于等于EstimatedRTT,否则,将造成不必要的重传,但是超时间隔也不能比EstimatedRTT大太多,否则当报文段丢失时,TCP不能很快地重传该报文段,导致传输延时过大。而且当SampleRTT值波动较大时,这个余量应该大些。所以DevRTT就有用武之地了,计算重传超时间隔的公式:

  TimeoutInterval = EstimatedRTT + 4 * DevRTT

在[RFC 6298]中推荐TimeoutInterval初始值为1秒,同样当出现超时后,TimeoutInterval值将加倍,以免即将被确认的后继报文段过早出现超时,一旦报文段收到更新的EstimatedRTT后,TimeoutInterval就又使用上述公式计算。

5.3可靠数据传输

TCP在IP不可靠的情况下尽力为服务创建一个可靠数据传输服务。TCP的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是无损坏、无间隔、非冗余和按序的数据流,即该字节流与连接的另一方端系统发送出的字节流是完全相同的。TCP提供可靠数据传输的方法在博客的第二部分有详细的技术功能描述。

TCP具体是如何提供可靠数据传输的其有三个主要事件:从上层应用程序接收数据、定时器、收到ACK。实际上其基本的技术都是建立博客第二部分描述的可靠数据传输协议的技术上,但是TCP相对第二部描述的技术应该进一步优化,下面来一段简单的TCP可靠数据传输处理流程和多种情况:

1.假设有A、B两个主机,A主机向B主机发送一个报文,报文段的序号是92,且含8个字节的数据。在发送该报文段之后,主机B准确的接收到了该报文的所有数据并给A主机发送了确认报文序号100。但是,确认报文在传输途中丢失,引发了A主机重发这段报文,并且也顺利的传输到了B主机,但是B主机原本就已经有了这段报文,所以直接丢弃重传的报文段。

2.A主机在给B主机发送第一段报文后,由于窗口长度允许继续发送后续报文,所以A主机又给B主机发送了第二段报文,序号是100,包含20个字节的数据。假设这两个报文都完好的被B主机接收,但是B主机给A主机发送的第一段确认报文丢失,导致A主机启动重发,并刷新定时器,如果第二段确认报文顺利的在定时器刷新后的间隔事件内被A主机收到,A主机就不会发送第二段报文了,这就是TCP的接收缓存的作用。

3.同样是情况二的数据传输,并且第一段确认报文丢失,但是第二段确认报文在第一段报文间隔时间之前就被A收到了,A也启动第一段报文的重发机制了。

 可靠数据传输实现机制

超时间隔加倍:超时间隔加倍这种机制为了控制拥塞,这种方式不只是存在TCP协议中,在以太网的CSMA/CD中也同样采用这种方式控制网络拥塞。这里还不详细介绍TCP的拥塞控制,后面会有详细TCP拥塞控制机制分析。但是在上面的TCP重传中已经引述出了超时间隔加倍,为了方便理解,就在这里阐述一下超时间隔加倍的原理机制。
比如在上面的例子中提到反馈报文丢失,TimeoutInterval就不是用EstimatedRTT和DevRTT的值来推算了,而是直接在原来的TimeoutInterval上加倍,并且如果第一次超时加倍后还是没有收到反馈报文,发送方在超时后重传报文段并且在第一次加倍的基础上再次加倍。(初始值:0.75秒,第一次超时:1.5秒;第二次超时:3秒)。这样就有效的防止了过多的分组被传入从源到目的端之间的路劲上,导致更大的网络延迟。
快速重传:超时触发重传存在的问题是超时周期可能相对较长,在这个时间里接收方可能会将超时报文段前一个确认报文重复反馈给发送方多次,这时就会出现ACK冗余现象,如果在超时周期之内一个报文段的ACK冗余数量得到三个,发送方就不会等超时间隔来触发重传了,而是提前实现重传。这就是快速重传。([RFC 5681])
TCP是回退N步还是选择重传
因为TCP的缓存机制,不需要回退N步全部重传,而是将超时或触发快速重传的报文段进行重传操作,重触发机制上来看很像GBN协议,但是并没有像GBN那样回退N步,而是有选择的重传操作,所以也可以说是SR协议,但是SR协议会向窗口传入疑似丢包或比特损坏的报文段,这与快速重传非常类似,但是又并行存在超时重传的GBN机制。TCP协议获得了SR协议与GBN协议的优势,所以可以说是GBN协议与SR协议的混合体。

 5.4流量控制

这里所谓的流量控制并不是控制网络流量MSS,也不是拥塞机制,而是控制TCP缓存的流浪,TCP的缓存空间不能溢出。这里需要关注的几个值:

LastByteRead:接收方的应用程序从TCP缓存中读出的最后一个字节符。
LastByteRcvd:接收方从网络中接收并放入缓存的最后一个字节符。
LastByteSent :发送方传入网络的最后一个字节符。
LastByteAcked:发送方发送的字节符但未被接收方确认放入缓存中的最小字节符。
RcvBuffer:接收方的缓存空间。
rwnd:接收方空闲的缓存空间。

所以得出以下公式:

LastByteRcvd - LastByteRead <= RcvBuffer     //TCP不允许已分配的缓存溢出。
rwnd = RcvBuffer - [ LastByteRcvd - LastByteRead]     //接收方空闲的缓存空间等于分配缓存空间 - 已被占用的缓存空间(已被占用的缓存空间等于已读最后一个字节符减去已放入缓存的最后一个字节符)
LastByteSent - LastByteAcked <= rwnd    //发送方最后发送的最后一个字节符减去未被接收方放入缓存的最小字节符要小于接收方的可用缓存空间

5.5TCP连接管理

关于TCP连接其实关于连接部分在前面已经多次出现了,指示解除连接没有介绍过,但是其本质上是确认机制实现的线程释放。也没有太多东西,直接上图:

TCP三次握手的过程解析:

1.客户端向服务端发送一段特殊报文,请求连接。(标志位SYN设置为1;)

2.服务端接收到请求连接报文后,返回一段特殊报文给客户端,建立连接。(SYN被设置为1;确认字段被加一:client_isn + 1;)

3.客户端收到服务端的连接建立确认报文后,客户端带着请求资源必须要的数据(也可以不带数据)发送一段特殊报文给服务端。(确认字段被加一:client_isn + 1;(在前面的基础之上加一))

上面三个步骤就是三次握手的过程,正式开始传输数据的时候SYN会被设置为0,接着来看四次挥手过程,也就是释放连接的过程:

TCP四次挥手的过程解析:

1.当客服端请求的数据全部接收到以后,客户端就会向服务端发送一个释放连接的报文段。报文中的FIN被设置为1。

2.服务端收到客户端的释放连接报文段后,向客户端发送一个确认报文段ACK。

3.服务端紧接着有向服务端发送一个释放连接的报文段,并且报文中的FIN也会被设置为1。

4.客户端接收到服务端的释放报文段后,又向服务端发送一个确认报文端ACK,告诉服务端收到了释放连接的确认报文。

 六、拥塞控制原理及TCP拥塞控制

 6.1出现拥塞

网络拥塞通俗的说就是网络传输需求大于网络传输能力,网络拥塞的三个一般性情况:

1.假设路由有无限大的缓存空间,发送端的发送速率好像就可以无限扩大,但是并非如此,链路完全中的排队分组也就会随着发送端的发送速率无限扩大,由于排队分组达到链路层的容量时,分组就会产生很大的排队延时,从而导致网络拥塞。

2.假设路由的容量是有限的,也就是当路由的缓存已满后就会丢弃后续被传入的分组,由于丢弃分组就会导致运输层重传,这就会进入一个恶性循环,丢弃越多,重传就会越多,延时就会越长,延时越长就会触发更多的重传,这种网络拥塞也可以说是超出供给载荷。

3.多个发送方在多个路由器之间的多跳路径,当其中一个路由其中了大量的传输容量时,途径这个路由的所有传输都会被拥塞。

 6.2拥塞控制方法

端到端拥塞控制:在这个方法中,网络层没有为运输层拥塞控制提供显式支持,即使网络中存在拥塞,端系统也必须通过对网络行为的观察来推断网络是否阻塞。TCP必须通过端到端的方法解决拥塞控制,因为IP层不会向系统提供有关网络拥塞的反馈信息。TCP报文的丢失(通过超时或3次冗余确认而得知)被认定是网络拥塞的一个迹象,TCP会相应的减少其窗口长度。在TCP拥塞控制的一些最新建议中,即使用增加往返时延值作为网络拥塞程度增加的指示。

网络辅助的拥塞控制:在网络辅助的拥塞控制中,网络层构件(即路由器)向发送方提供有关网络中拥塞状态的显示反馈信息。拥塞信息从网络反馈到发送方通常有两种方式:①直接反馈信息由网络路由器发给发送方,这种通知方式常采用了一种阻塞分组(choke packet)的形式(含义为:“我阻塞了”)②显示拥塞通知(Explicit Congestion Notification,ECN)。第二种是路由器标记或更新熊发送方流向接收方的分组中的某个字段来指示拥塞的产生。当接收方接收到这样的分后后,就会向发送方发送网络拥塞的通知。这种方式需要至少需要一个完整的RTT。使用网络辅助的拥塞控制例子可参见ATM ABR拥塞控制。

6.3TCP拥塞控制

在TCP的拥塞控制机制上,TCP使用的是端到端的拥塞控制。即让每个发送方根据感受到的网络拥塞程度限制其能向其连接发送流量的速率。由此引发三个问题:1.TCP发送方如何限制它向其连接发送流量的速率。2.TCP发送方如何感知它到目的地之间的路径出现了拥塞。3.当发送方感受到了端到端的时延使用何种算法来改善其发送速率。

6.3.1TCP发送方如何限制它向其连接发送流量的速率?
TCP连接的每一个端都有一个接收缓存、一个发送缓存、和几个变量组成。运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即拥塞窗口(cwnd)。TCP发送方通过cwnd来控制发送流量速率,也就是未被确认的数据流量不能超过cwnd,所以也必然不会超过rwnd(接收方空闲缓存空间)。

LastByteSent - LastByteAcked <= min{cwnd, rwnd}

上面这个公式就是通过拥塞窗口控制发送方的发送流量速率的基本原理,假设rwnd无限容量,但是发送流量速率受到已发送发送未确认分组(已发送的最大字节符序号减去未被确认的最小字节符序号)的控制,也就间接的限制了发送流量速率。拥塞窗口长度校验算法

所以发送方的发送流量速率可以估算为:在不考虑丢包和发送时延的情况下,发送速率大概是cwnd/RTT(字节/秒)。

6.3.2TCP发送方如何感知它到目的地的路径出现了拥塞?

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

从判断窗拥塞中可以反向思考,如果发送方传输的报文段都正常被接收方接收到,也就是说都能正常的接收到接收方反馈的确认报文ACK,这样的情况下就可以被判定为时网络传输通畅,发送方就会增加拥塞窗口的长度,从而达到提高传输速率的效果,而却会根据反馈确认报文的ACK的RTT来判断网络通畅程度,如果确认已相当慢的速率到达就,速率增长也同样会以相当慢速度增长。反之,如果确认相对快的速率到达,拥塞窗口就会瞬间增大窗口长度。TCP把这种机制叫做自计时。

6.3.3当发送方感受到了端到端的时延使用何种算法来改善其发送速率?

 概述了TCP拥塞控制后,现在就需要采用对应的方法来改善发送速率,广受赞誉的TCP拥塞控制算法[Jacobson 1988]。该算法包含了三个重要的部分:1、启动慢;2、拥塞避免;3、快速恢复。慢启动和拥塞控制是TCP的强制部分,两者的差异在于对收到的ACK做出反应时增加cwnd长度的方式。慢启动比拥塞控制能更快的增加cwnd的长度。快速恢复是推荐部分,对TCP发送方并非必须的。

6.3.3.1慢启动

在慢启动状态,cwnd的值以一个MSS开始并当传输的报文首次被确认就增加一个MSS。如下图所示,开始发送一个报文段,收到确认后拥塞窗口增加1。然后传输2个报文段,收到2个确认后增加拥塞窗口变成了4个MSS。这样没经过一个RTT,发送速率就会翻番。于是,TCP发送的起始速率慢,但是在慢启动阶段会以指数增长。

但是这样会的增长何时终止呢?慢启动对这个问题提供了几种答案:

1. 如果出现一个有超时引起的丢包事件(即网络中出现了拥塞),TCP发送方将cwnd设置为1并重新开始慢启动过程。它还会将第二个状态变量ssthresh(“慢启动阈值”)设置为cwnd/2。
2.与ssthresh相关。当增加到cwnd=ssthresh时,结束慢启动并开始拥塞避免。
3.如果检测到3个冗余ACK,这时TCP执行快速重传进入快速恢复状态。

 6.3.3.2拥塞避免

进入拥塞避免状态后,cwnd的值大约是上次遇到拥塞时的值的一般,即距离拥塞可能并不遥远。TCP可定不能再像慢启动那样对cwnd的值翻番,而是采用了另一种方式更新cwnd的值。当TCP发送方无论何时达到一个新的确认,就将cwnd增加一个MSS字节(MSS/cwnd)。例如,如果MSS是1460个字节并且cwnd也是1460字节,则在一个RTT内发送10个报文段。每个到达ACK增加1/10MSS的拥塞窗口长度,因此,在收到所有10个报文端的确认后,拥塞窗口的值将增加一个MSS。

那拥塞避免的线性增长在什么时候停止呢?跟启动慢一样,再出现丢包事件后cwnd的值同样会被更新为cwnd值的一半。如果丢包事件是由三个冗余ACK事件触发,在这种情况TCP会进入快速恢复状态。

6.3.3.3快速恢复

在前面的TCP可靠数据传输部分就有提到快速重传,相当于是快速恢复的开始。在快速恢复中,对引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK,cwnd的值增加一个MSS。最终,当丢失报文段到达时,TCP再降低cwnd后进入拥塞避免状态。如果出现超时事件,cwnd置为1个MSS,并且ssthresh置为cwnd的一半,迁移到慢启动。

快速恢复是TCP推荐部件而不是必需。一种早期的TCP版本TCP Tahoe,不管是超时而引起的丢包还是3个冗余ACK引起的丢包事件,都会将cwnd置为1个MSS,并进入慢启动阶段。TCP较新的版本TCP Reno综合了快速恢复算法。

 

posted @ 2022-10-03 02:28  他乡踏雪  阅读(505)  评论(0编辑  收藏  举报