本章学习运输层
运输层位于应用层和网络层之间,是分层的网络体系的重要部分,该层为运行在不同主机上的应用进程提供直接的通信服务起着至关重要的作用。
运输层协议为运行在不同主机上的应用进程之间提供了逻辑通信(logic communication)功能。从应用程序的角度看,通过逻辑通信,运行不同进程之间的主机好像直接相连一样;实际上这些主机之间通过很多路由器及多种不同类型的链路相连,下图图示了逻辑通信的概念
如图,运输层协议是在端系统中而不是在路由器中实现的。在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组(运输层报文段),实现的方法是将应用报文划分为较小的块,并为每块加上一个运输层首部以生成运输层报文段。然后在发送端系统中,运输层将这些报文段传递给网络层,网络层将其封装成网络层分组(数据报)并向目的地发送。【注意:网络路由器仅作用于该数据报的网络层字段;即它们不检查封装在该数据报的运输层报文段的字段】在接收端,网络层从数据报中提取运输层报文段,并将该报文段向上交给运输层。运输层则处理接收到的报文段,使该报文段中的数据为接收应用进程使用。
运输层和网络层之间的关系
在协议栈中,运输层刚好位于网络层之上。网络层提供了主机之间的逻辑通信,而运输层为在不同主机上的进程之间提供了逻辑通信。
举例 有两个家庭,每个家庭有12个孩子,每周他们要互相写一封信,因此每个家庭每周向另一家发送144封信,每一个家庭有一个孩子负责收发邮件,Anna和Bill,每星期Anna去她的所有兄弟姐妹那里收集邮件,并将这些邮件交到邮政服务的邮车上,同时她还负责接收并分发邮件。Bill也是负责这样的工作。
在这个例子中,邮政服务为两个家庭间提供逻辑通信,邮政服务将信件从一家送往一家,而不是从一个人送往另一个人,另一方面,Anna和Bill为兄妹之间提供了逻辑通信,Anna和Bill从兄弟姐妹那里收取或分发邮件,从兄弟姐妹的角度看,Anna和Bill就是邮件服务,尽管他们只是端到端交付过程中的一部分
应用层报文=信封上的字符 |
进程=兄弟姐妹 |
主机(端系统)=家庭 |
运输层协议=Anna和Bill |
网络层协议=邮政服务(包括邮车) |
运输层协议只工作在端系统中,运输层协议将来自应用进程的报文移动到网络边缘(即网络层),但对这些报文在网络核心如何移动并不作任何规定。
假定Anna和Bill外出度假,另外一对姐妹Alice和Bob接替他们的工作。但是Alice和Bob与Anna和Bill所做的并不完全一样,比如收发邮件的次数更少、偶尔可能还会丢失邮件等等。与此类似计算机网络中可以安排多种运输层协议,每种协议为应用程序提供不同的服务模型。
Anna和Bill的提供的服务明显受限于邮政服务所能提供的服务,与此类似,运输协议能够提供的服务常常受制于底层网络协议的服务模型,如果网络层协议无法为主机之间发送的运输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用程序报文提供时延或带宽保证。
运输层概述
因特网为应用层提供了两种截然不同的可用运输层协议,一种是UDP(用户数据报协议),它为调用它的应用程序提供一种不可靠、无连接的服务。另一种是TCP(传输控制协议),它为调用它的应用程序提供了可靠的、面向连接的服务。
运输层分组称为报文段(segment),网络层分组称为数据报(datagram)
因特网网络层协议中有一个名字叫IP(网际协议)。IP为主机之间提供了逻辑通信,IP的服务模型是尽力而为交付服务。这意味着 它不做任何确保,不确保报文段的交付、不确保完整性和有序性。因此IP被称为不可靠服务
UDP和TCP最基本的职责是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务,将主机间交付扩展到进程间交付被称为运输层的多路复用与多路分解,进程到进程的数据交付和差错检查是两种最低限度的运输层服务,也是UDP所能提供的仅有的两种服务
另一方面,TCP为应用程序提供可靠数据传输。通过使用流量控制、序号、确认和定时器,TCP确保正确地、按序地将数据从发送进程交付给接收进程,这样TCP就将两个端系统之间的不可靠IP服务转换成了一种进程间地可靠数据传输服务。同时TCP还提供拥塞控制。
多路复用与多路分解
这里我们讨论运输层的多路复用与多路分解,也就是将由网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务。
在目的主机,运输层从紧邻其下的网络层接收报文段,运输层负责将这些报文段中的数据交付给在主机上运行的适当应用程序进程。
一个进程(作为网络应用的一部分)有一个或多个套接字,它相当于从网络向进程传递数据和从进程向网络传递数据的门户。因此如图所示,在接收主机中的运输层实际上并没有直接将数据交付给进程,而是将数据交给了一个中间套接字。
那么主机怎样将一个到达的运输层报文段定向到适当的套接字。为此,每个运输层报文段中具有几个字段。在接收端,运输层检查这些字段标识出接收套接字,进而将报文段定向到该套接字。将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层,所有这些工作称为多路复用。
在主机中”多路复用与多路分解“实际是怎样工作的?在前面我们已经知道运输层多路复用要求:①套接字有唯一标识符;②每个报文段有特殊字段来指示该报文段所要交付的套接字
如下图所示,这些特殊字段是源端口号字段(source port number field)和目的端口号字段(destination port number field)。端口号是一个16比特的数,其大小在0~65535之间。0~1023范围的端口号称为周知端口号(well-known port number),是受限制的,这是指他们保留给诸如HTTP(它使用端口号80)和FTP(它使用端口号21)之类的周知应用层协议来使用。
现在我们应该清楚运输层是怎样能够实现分解服务的了:在主机上的每个套接字能够分配一个端口号,当报文段到达主机时,运输层检查报文段中的目的端口号,并将其定向到相应的套接字。然后报文段中的数据通过套接字进入其所连接的进程。
无连接的多路复用与多路分解
【假定在主机A中的一个进程具有UDP端口19157,它要发送一个应用程序数据块给位于主机B中的另一进程,该进程具有UDP端口46428.主机A中的运输层创建一个运输层报文段,其中包括应用程序数据、源端口号(19157)、目的端口号(46428)和两个其他值,然后运输层将得到的报文段传递到网络层。网络层将该报文段封装到一个IP数据报中,并尽力而为地将报文段交付给接收主机。如果该报文段到达接收主机B,接收主机运输层就检查该报文段中的目的端口号(46428)并将该报文段交付给端口号46428所标识的套接字。需要注意的是,主机B能够运行多个进程,每个进程有自己的UDP套接字及相应的端口号,当UDP报文段从网络到达时,主机B通过检查该报文段中的目的端口号,将每个报文段定向(分解)到相应的套接字】
一个UDP套接字是由一个二元组全面标识的,该二元组包含一个目的IP地址和一个目的端口号。如果两个UDP报文段有不同的源IP地址或源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将通过相同的套接字被定向到相同的目的进程。
源端口号的用途是什么?
在下图中,在A到B的报文段中,源端口号用作”返回地址“的一部分,即当B需要回发一个报文段给A时,B到A的报文段中的目的端号便从A到B的报文段中的源端口号中取值
面向连接的多路复用与多路分解
TCP套接字和UDP套接字之间的差别是,TCP套接字是由一个四元组(源IP地址,源端口号,目的IP,目的端口号)来标识的。因此当一个TCP报文段从网络到达一台主机时,该主机使用全部4个值来将报文段定向(分解)到相应的套接字。与UDP不同的是,两个具有不同源IP地址或源端口号的到达TCP报文段将被定向到两个不同的套接字,除非TCP报文段携带了初始创建连接的请求。
下图就是这种情况,【主机C向服务器B发起了两个HTTP会话,主机A向服务器B发起一个HTTP会话。主机A与主机C及服务器B都有自己唯一的IP地址,它们分别是A、C、B。主机C为其两个HTTP连接分配了两个不同的源端口号(26145和7532),因为主机A选择源端口号时与主机C互不相干,因此也可以将源端口号26145分配给其HTTP连接,但由于它们连接有不同的源IP地址,服务器B仍然能够正确地分解这两个具有相同源端口号的连接】
无连接运输:UDP
UDP从应用进程得到数据,附加上用于多路复用/分解服务的源和目的端号字段,以及两个其他的小字段,然后将形成的报文段交给网络层。网络层将该运输层报文段封装到一个IP数据报中,然后尽力而为地尝试将此报文段交付给接收主机。如果该报文段到达接收主机,UDP使用目的端口号将报文段中的数据交付给正确的应用进程。需要注意的是,使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有握手,正因为如此,UDP被称为是无连接的。
有许多应用更适合用UDP,原因主要有:
①关于发送什么数据以及何时发送的应用层控制更为精细。采用UDP时,只要应用进程将数据传递给UDP,UDP就会将此数据打包进UDP报文段并立即传递给网络层。而TCP有一个拥塞控制机制,而且由于它的可靠交付,使得一些实时应用不适合使用TCP服务模型
②无需建立连接。TCP在开始数据传输之前要经过三次握手,UDP却不需要任何准备即可进行数据传输。因此UDP不会引入建立连接的时延
③无连接状态。TCP需要在端系统中维护连接状态,此连接状态包括接收和发送缓存、拥塞控制参数以及序号与确认号的参数;UDP不维护连接状态,也不跟踪这些参数
④分组首部开销小。每个TCP报文段都有20字节的首部开销,而UDP仅有8字节的开销
下表列出了流行的因特网应用及所使用的运输协议,电子邮件、远程终端访问、Web及文件传输都运行在TCP上。因为这些应用都需要TCP的可靠数据传输服务。
UDP报文段结构
UDP报文段结构如下,应用层数据占用UDP报文段的数据字段,UDP首部只有4个字段,每个字段由两个字节组成。通过端口号可以使目的主机将应用数据交给运行在目的端系统中的相应进程。字段长度指示了在UDP报文段中的字节数。因为数据字段的长度在一个UDP段中不同于在另一个段中,故需要一个明确的长度,接收方使用检验和来检查在该报文段中是否出现差错。
UDP检验和
UDP检验和提供了差错检测功能。这就是说,检验和用于确定当UDP报文段从源到达目的地移动时,其中的比特是否发生了改变。发送方的UDP对报文段中的所有16比特字的和进行反码运算,求和时遇到的任何移除都被回卷(就是把溢出的最高位1和低16位相加),得到的结果被放在UDP报文段中的检验和字段。
假定有下面3个16比特的字:
0110011001100000 |
0101010101010101 |
1000111100001100 |
前两个之和是:
0110011001100000 |
0101010101010101 |
1011101110110101 |
再将上面的和与第三个字相加:
1011101110110101 |
1000111100001100 |
0100101011000010 |
最后一次假发有溢出,它要被回卷。该和的反码运算结果是1011010100111101,这就变成了检验和,在接收方全部的4个16比特字(包括检验和)加在一起,如果该分组中没有引入差错,则显示在接收方处该和将是1111111111111111,如果其中有一位是0,那么该分组已经出现了差错。
用户数据报UDP有两个字段:数据字段和首部字段。首部字段很简单,只有8个字节,由四个字段组成,每个字段的长度都是两个字节。
各个字段意义如下:
①源端口 源端口号。在需要对方回信时选用。不需要时可全用0
②目的端口 目的端口号。这在终点交付报文时必须使用
③长度 UDP用户数据报的长度,其最小值是8(仅有首部)
④检验和 检测UDP用户数据报在传输中是否有错。有错就丢弃
计算检验和的思路
计算检验和->伪首部、首部、数据求和->前两者反码运算->0为错误1为正确
UDP用户数据报首部中检验和的计算方法比较特殊。在计算检验和时,要在UDP用户数据报之前增加12个字节的伪首部;
伪首部既不向下传送也不向上递交,而仅仅是为了计算检验和;
UDP计算检验和的方法和计算IP数据报首部检验和的方法相似,但不同的是:IP数据报的检验和只检验IP数据报的首部,但UDP的检验和是把首部和数据部分一起都检验
过程如下:
在发送方,首先是先把全零放入检验和字段。再把伪首部以及UDP用户数据报看成是由许多16位的字串接起来。若UDP用户数据报的数据部分不是偶数个字节,则要填入一个全零字节(但此字节不发送),然后按二进制反码计算出这些16位字的和。将此和的二进制反码写入检验和字段后,就发送这样的UDP用户数据报。
在接收方,把收到的UDP用户数据报连同伪首部(以及可能的填充全零字节)一起,按二进制反码求这些16位字的和。当无差错时其结果应全为1.否则就表明有差错出现,接收方就应丢弃这个UDP用户数据报
可靠数据传输原理
为上层实体提供的服务抽象是:数据可以通过一条可靠的信道进行传输。借助于可靠信道,传输数据比特就不会受到损坏,而且所有数据都是按照其发送顺序进行交付,这恰好就是TCP向调用它的因特网应用所提供的服务模型
实现这种服务抽象是可靠数据传输协议的责任。 由于可靠数据传输协议的下层协议也许是不可靠的,因此这是一项困难的任务。
构造可靠数据传输协议
1.经完全可靠信道的可靠数据传输:rdt1.0
下图显示了rdt1.0发送方和接收方的有限状态机(Finite-State Mashine,FSM)的定义,发送方和接收方有各自的FSM
发送方和接收方的FSM每个都只有一个状态;
rdt的发送端只通过rdt_send(data)事件接受来自交稿层的数据,产生一个包含该数据的分组并将分组发送到信道中;
在接收端,rdt通过rdt_rcv(packet)事件从底层信道接收一个分组,从分组中取出数据并将数据上传给较高层;
在这个简单的协议中,有了完全可靠的信道,接收端就不需要提供任何反馈信息给发送方。
2.经具有比特差错信道的可靠数据传输:rdt2.0
底层信道更为实际的模型是分组中的比特可能受损的模型。
(考虑一下你自己是怎样通过电话口述一条长报文的?通常接收者在听到并记下每句话后可能会说"ok",如果没听清,他可能会要求你重复一遍刚才的话。这种口述报文协议使用了“肯定确认”与”否定确认“,这使得接收方可以让发送方知道哪些内容被正确接收,哪些内容接收有误需要重复),在计算机网络中,基于这样重传机制的可靠数据传输协议称为自动重传请求(Automatic Repeat reRuest,ARQ)协议
重要的是,ARQ协议还需要另外三种协议功能来处理存在比特差错的情况:
①差错检测 需要一种机制以使接收方检测到何时出现了比特差错;
②接收方反馈 发送方和接收方通常在不同端系统上执行,发送方要了解接收方情况的唯一途径就是让接收方提供明确的反馈信息给发送方;rdt2.0协议向接收方发送ACK(肯定确认)与NAK(否定确认)分组来实现反馈;
③重传 接收方收到有差错的分组时,发送方将重传该分组;
下图说明了rdt2.0的FSM,该数据传输协议采用了差错检测、肯定确认与否定确认。
rdt2.0的发送端有两个状态。当发送方处于等待ACK或NAK的状态时,它不能从上层获得更多的数据,仅当接收到ACK并离开该状态时才能从上层获得数据。由于这种行为,rdt2.0这样的协议被称为停等协议。
接收方的FSM仍只有单一状态,接收方要么回答一个ACK,要么回答一个NAK。
似乎这样就可以运行了。但是如果ACK或NAK分组受损,发送方无法知道接收方是否正确接收了上一块发送的数据。
解决这个问题的一个简单方法(几乎所有现有的数据传输协议中,包括TCP,都采用了这种方法)是在数据分组中添加一新字段,让发送方对其数据分组编号,即将发送数据分组的序号放在该字段。
下图给出rdt2.1的FSM描述。
3.经具有比特差错的丢包信道的可靠数据传输:rdt3.0
假定除了比特受损外,底层信道还会丢包,所以应该关注两个问题:怎样检测丢包以及发生丢包后该做些什么?
从发送方的观点来看,重传是一种万能灵药。发送方不知道是一个分组丢失还是一个ACK丢失,它的重传操作都可以解决,为了实现基于时间的重传机制,需要一个倒计时定时器,在一个给定的时间量过期后,可中断发送方。因此发送方需要做到①每次发送一个分组(包括第一次分组和重传分组)时,便启动一个定时器②响应定时器中断(采取适当的动作)③终止定时器。
流水线可靠数据传输协议
rdt3.0性能问题的核心在于它是一个停等协议。
这种特殊的性能问题的一个简单解决方法是:不以停等方式运行,允许发送方发送多个分组而无须等待确认。因为许多从发送方向接收方输送的分组被看成是填充到一条流水线中,故这种技术被称为流水线。
- 必须增加序号范围,因为每个输送中的分组(不计算重传的)必须有一个唯一的序号,而且也许有多个在输送中的未确认报文
- 协议中的发送方和接收方两端也许不得不缓存多个分组,发送方最低限度应当能缓冲那些已发送但没有确认的分组,接收方也许需要缓存那些已经正确接收的分组
- 所需序号范围和对缓冲的要求却决于数据传输协议如何处理丢失、损坏及延时过大的分组解决流水线的差错恢复有两种基本方法是:回退N步(Go-Back-N,GBN)和选择重传(Selective Repeat,SR)
回退N步
在回退N步(GBN)协议中,允许发送方发送多个分组而不需要等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。
下图显示了发送方看到的GBN协议的序号范围。如果将基序号(base)定义为最早未确认分组的序号,将下一个序号(nextseqnum)定义为最小的未使用序号(即下一个待发分组的序号),则可将序号范围分割为4段。在[0,base-1]段内的序号对应于已经发送并确认的分组。[base,nextseqnum-1]段内对应已经发送但未被确认的分组。[nextseqnum,base+N-1]段内的序号用于那些要被立即发送的分组,最后大于或等于base+N的序号是不能使用的,直到当前流水线中未被确认的分组(特别是序号为base的分组)已得到确认为止。
那些已被发送但还未被确认的分组的许可序号范围可以被看成是一个在序号范围内长度为N的窗口,随着协议的运行,该窗口在序号空间向前滑动。因此N常被称为窗口长度,GBN协议也常被称为滑动窗口协议。
GBN发送方必须响应三种类型的事件:
①上层的调用。当上层调用rdt_send()时,发送方首先检查发送窗口是否已满,即是否有N个已发送但未被确认的分组。如果窗口未满,则产生一个分组并将其发送,并相应地更新变量。如果窗口已满,发送方只需将数据返回给上层,隐式地指示上层该窗口已满。然后上层可能会过一会儿再试。
②收到一个ACK。在GBN协议中,对序号为n的分组的确认采用累计确认的方式,表明接收方已正确接收到序号为n的以前且包括n在内的所有分组。
③超时事件。协议的名字“回退N步”来源于出现丢失和时延过长分组时发送方的行为。就像在停等协议中那样,定时器将再次用于恢复数据或确认分组的丢失。如果出现超时,发送方重传所有已发送但还未被确认的分组
在GBN中,接收方的动作也很简单。如果一个序号为n的分组被正确接收到,并且按序,则接收方为分组n发送一个ACK,并将该分组中的数据部分交付到上层
选择重传
GBN协议潜在地允许发送方用多个分组“填充流水线”,因此避免了停等协议中所提到的信道利用率问题,然而GBN本身也有一些情况存在着性能问题,尤其是当窗口长度和带宽时延积都很大时,在流水线中会有很多分组更是如此,单个分组的差错就能因此GBN重传大量分组,许多分组根本没有必要重传。
顾名思义,选择重传(SR)协议通过让发送方仅重传那些它换衣在接收方出错(即丢失或受损)的分组而避免了不必要的重传。这种个别的、按需的重传要求接收方逐个地确认正确接收的分组。再次用窗口长度N来限制流水线中未完成的、未被确认的分组数,然而与GBN不同的是,发送方已经收到了对装口中某些分组的ACK。下图显示了SR发送方看到的序号空间。
SR接收方将确认一个正确接收的分组而不管其是否按序。失序的分组将被缓存直到所有丢失分组皆被收到为止,这时才可以将一批分组按序交付给上层。
SR发送方的事件与动作
①从上层收到数据。当从上层收到数据后,SR发送方检查下一个可用于该分组的序号。如果序号位于发送方的窗口内,则将数据打包并发送;否则就像在GBN中一样,要么将数据缓存,要么将其返回给上层以便以后传输
②超时。定时器再次被用来防止丢失分组。然而,现在每个分组必须拥有其自己的逻辑定时器,因为超时发生后只能发送一个分组。
③收到ACK。如果收到ACK,倘若该分组序号在窗口内,则SR发送方将那个被确认的分组标记为已接收。如果该分组的序号等于send_base,则窗口基序号向前移动到具有最小序号的未确认分组处。如果窗口移动了并且有序号落在窗口内的未发送分组,则发送这些分组。
机制 | 用途和说明 |
检验和 | 用于检测在一个分组中的比特错误 |
定时器 | 用于超时/重传一个分组,可能因为该分组(或其ACK)在信道中丢失了。由于当一个分组延时但未丢失,或当一个分组已被接收方收到但从接收方到发送方的ACK丢失时,可能产生超时事件,所以接收方可能会收到一个分组的多个冗余副本 |
序号 | 用于为从发送方流向接收方的数据分组按顺序编号。所接受分组的序号间的空隙可使接收方检测出丢失的分组。具有相同序号的分组可使接收方检测出一个分组的冗余副本 |
确认 | 接收方用于告诉发送方一个分组或一组分组已被正确接收到了。确认报文通常携带着被确认的分组或多个分组的序号。确认可以是逐个的或累积的,这取决于协议 |
否定确认 | 接收方用于告诉发送方某个分组未被正确地接收。否定确认报文通常携带着未被正确接受的分组的序号 |
窗口、流水线 | 发送方也许被限制仅发送那些序号落在一个指定范围内的分组。通过允许一次发送多个分组但未被确认,发送方的利用率可在停等操作模式的基础上得到增加,窗口长度可根据接收方接收和缓存报文的能力、网络中的拥塞程度或两者情况来进行设置 |
面向连接的运输:TCP
TCP是因特网运输层的面向连接的可靠的运输协议
TCP连接
TCP被称为是面向连接的,这是因为在一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立确保数据传输的参数。这种TCP连接不像在电路交换网络中的电路,该“连接”是一条逻辑连接,其共同状态仅保留在两个通信端系统的TCP程序中,由于TCP协议只在端系统中运行,而不在中间的网络元素中运行,所以中间的网络元素不会维持TCP连接状态
TCP连接提供的是全双工服务(full-duplex service);如果一台主机上的进程A与另一台主机上的进程B存在一条TCP连接,那么应用数据可以在AB之间互传。TCP连接也总是点对点(point-to-point)的,即在单个发送方与单个接收方之间的连接
TCP连接是怎样建立的?(发起连接的进程叫客户进程,另外一个进程叫服务器进程)客户首先发送一个特殊的TCP报文段,服务器用另一个特殊的TCP报文段来响应,最后客户再用第三个特殊报文段作为响应。前两个报文段不包含应用层数据,而第三个报文段可以承载有效载荷,由于在这两台主机之间发送了3个报文段,所以这种连接建立过程被称为“三次握手”
TCP连接的组成包括:一台主机上的缓存、变量和与进程连接的套接字,以及另一台主机上的另一组缓存、变量和与进程连接的套接字。在这两台主机之间的网络元素(路由器、交换机和中继器)中,没有为该连接分配任何缓存和变量。
客户进程通过套接字传递数据流,数据一旦通过该门,它就由客户中运行的TCP控制,TCP为每块客户数据配上一个TCP首部,从而形成多个TCP报文段,这些报文段被下传给网络层,网络层将其分别封装在网络层IP数据报中,然后这些IP数据报被发送到网络中。当TCP在另一端接收到一个报文段后,该报文段的数据就被放入该TCP连接的接收缓存中,如下图所示应用程序从此缓存中读取数据流,该连接的每一端都有各自的发送缓存和接收缓存。
TCP报文段结构
TCP报文段由首部字段和一个数据字段组成。数据字段包含一块应用数据。当TCP发送一个大文件时,由于最大报文段长度(Maximum Segment Size,MSS)的限制,TCP通常将该文件划分为长度为MSS的若干块。
下图显示了TCP报文段的结构。与UDP一样,首部包括源端口号和目的端口号,它被用于多路复用/分解来自或送到上层应用的数据。同样地,TCP首部也包括检验和字段。
TCP报文段首部还包含下列字段:
- 32比特的序号字段(sequence number field)和32比特的确认号字段(acknowledgement number field),这些字段被TCP发送方和接收方用来实现可靠数据传输服务
- 16比特的接收窗口字段(receive window field),该字段用于流量控制,用于指示接收方愿意接受的字节数量
- 4比特的首部字段长度(header length field),该字段指示了以32比特的字为单位的TCP首部长度。TCP首部的长度是可变的,典型长度是20字节
- 可选与变长的选项字段,该字段用于发送方与接收方协商最大报文段长度时使用
- 6比特的标志字段。ACK比特用于指示确认字段中的值是有效的,RST、SYN和FIN比特用于连接建立和拆除。
序号和确认号
TCP报文段首部中两个最重要的字段是序号字段和确认号字段,这两个字段是TCP可靠传输服务的关键部分。
TCP把数据看成一个无结构的、有序的字节流。从TCP对序号的使用上可以看出,因为序号是建立在传送的字节流之上,而不是建立在传送的报文段的序列之上。一个报文段的序号因此是该报文段首字节的字节流编号。假设主机A上的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流,主机A中的TCP将隐式地对数据流中的每一个字节编号,假定数据流由一个包含500000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0。如下图所示,该TCP将该数据流构建500个报文段,给第一个报文段分配序号0,第二个报文段分配序号1000,第三个报文段分配序号2000,以此类推每一个序号被填入到相应TCP报文段首部的序号字段中。
现在我们考虑一下确认号。TCP是全双工的,因此主机A在向主机B发送数据的同时,也许也接收主机B的数据。主机A填充进报文段的确认号是主机A期望从主机B收到的下一字节的序号。
可靠数据传输
因特网的网络层服务(IP服务)是不可靠的。IP不保证数据报的交付,不保证数据报的按序交付,也不保证数据的完整性。对于IP服务,数据报能够溢出路由器缓存而永远不能到达目的地,数据报也可能是乱序到达,而且数据报中的比特可能损坏。由于运输层报文段是被IP数据报携带着在网络中传输的,所以运输层的报文段也会遇到这些问题。
TCP在IP不可靠的尽力而为服务之上创建了一种可靠数据传输服务(reliable data transfer service)。TCP的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是无损坏、无间隙、非冗余和按序的数据流。
TCP发送方描述
TCP发送方有3个与发送和重传有关的主要事件:从上层应用程序接收数据;定时器超时和收到ACK。
第一个主要事件发生,TCP从应用程序接收数据,将数据封装在一个报文段中,并把该报文段交给IP;
第二个主要事件是超时。TCP通过重传引起超时的报文段来响应超时事件,然后TCP重启定时器;
第三个主要事件是,到达一个来自接收方的确认报文段ACK。当该事件发生时,TCP将ACK的值y与它的变量SendBase进行比较。TCP状态变量SendBase是最早未被确认的字节的序号。(因此SendBase-1是指接收方已正确按序接收到的数据的最后一个字节的序号),TCP采用累计确认,所以y确认了字节编号在y之前的所有字节都已经收到。如果y大于SendBase,则该ACK是在确认一个或多个先前未被确认的报文段,因此发送方更新它的SendBase变量,如果当前有未被确认的报文段,TCP还要重新启动定时器。
快速重传
超时触发重传存在的问题之一是超时周期可能相对较长,因而增加了端到端时延。但是,发送方通常可以在超时事件发生之前通过冗余ACK来检测到丢包情况。冗余ACK就是再次确认某个报文段的ACK,而发送方先前已经收到对该报文段的确认。
当TCP接收方收到一个具有这样序号的报文段时,即其序号大于下一个所期望的、按序的报文段,它检测到了数据流中的一个间隔,这就是说有报文段丢失,这个间隔可能是由于在网络中报文段丢失或重新排序造成的。因为TCP不使用否定确认,所以接收方不能向发送方发挥一个显式的否定确认,相反它只是对已经接收到的最后一个按序字节数据进行重复确认(即产生一个冗余ACK)即可
事件 | TCP接收方动作 |
具有所期望序号的按序报文段到达。所有在期望序号及以前的数据都已经被确认 | 延迟的ACK。对另一个按序报文段的到达最多等待500ms,如果下一个按序报文段在这个时间间隔内没有到达,则发送一个ACK |
具有所期望序号的按需报文段到达。另一个按序报文段等待ACK传输 | 立即发送单个累计ACK,以确认两个按序报文段 |
比期望序号大的失序报文段到达。检测出间隔 | 立即发送冗余ACK,指示下一个期待字节的序号(其为间隔的低端的序号) |
能部分或完全填充接收数据间隔的报文段到达 | 倘若该报文段起始于间隔的低端,则立即发送ACK |
因为发送方经常一个接一个地发送大量的报文段,如果一个报文段丢失,就很可能引起许多一个接一个的冗余ACK。如果TCP发送方接收到对相同数据的3个冗余ACK,它把这当作一种指示,说明跟在这个已被确认过3次的报文段之后的报文段已经丢失,一旦收到3个冗余ACK,TCP就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。
流量控制
一条TCP连接的每一侧主机都为该连接设置了接收缓存。当该TCP收到正确、按序的字节后,它将该数据放入接收缓存,相关联的应用进程会从该缓存中读取数据,但不必时数据刚一到达就立即读取。如果某应用程序读取数据时相对缓慢,而发送方发送得太多、太快,发送的数据就会很容易地使该连接的接收缓存溢出。
TCP为它的应用程序提供了流量控制服务以消除发送方使接收方缓存溢出的可能性。流量控制因此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。
TCP通过让发送方维护一个称为接收窗口的变量来提供流量控制。通俗的说,接收窗口用于给发送方一个指示——该接收方还有多少可用的缓存空间。因为TCP使全双工通信,在连接两端的发送方都各自维护一个接收窗口
TCP连接管理
我们更为仔细地观察如何建立和拆除一条TCP连接
假设运行在一台主机(客户) 上的一个进程想与另一台主机(服务器)上的一个进程建立一条连接
第一步:客户端的TCP首先向服务器端的TCP发送一个特殊的TCP报文段。该报文段中不包含应用层数据,但是在报文段的首部中的一个标志位(SYN比特)被置为1,因此这个特殊报文段被称为SYN报文段,另外客户会随机地选择一个初始序号(client_isn),并将此编号放置在该起始地TCP SYN报文段的序号字段中。该报文段会被封装在一个IP数据报中,并发送给服务器。
第二步:一旦包含TCP SYN报文段的IP数据报到达服务器主机,服务器会从该数据报中提取出TCP SYN报文段。这个报文段的首部包含3个重要信息。首先SYN比特被置为1,其次该TCP报文段首部的确认号字段被置为client_isn+1,最后服务器选择自己的初始序号(server_isn),并将其放置到TCP报文段首部的序号字段中。这个允许连接的报文段实际上表明了:“我收到了你发起建立的SYN分组,该分组带有初始序号client_isn,我同意建立该连接,我自己的初始序号是server_isn"。该允许连接的报SYNACK报文段
第三步:在收到SYNACK报文段后,客户也要给该连接分配缓存和变量。客户主机则向服务器发送另外一个报文段;这最后一个报文段对服务器的允许连接的报文段进行了确认。因为连接已经建立,所以该SYN比特被置为0。该三次握手的第三个阶段可以在报文段负载中携带客户到服务器的数据。
一旦完成这3个步骤,客户和服务器主机就可以相互发送包括数据的报文段了,在以后每一个报文段中,SYN比特都将被置为0。为了建立该连接,两台主机之间发送了3个分组,因此这种连接创建过程通常被称为3次握手。
为什么需要3次握手而不是两次握手?
在第2步中,服务器端将自己选择的初始序号发送给客户端,因此客户端需要在第三步中向服务器端确认该序号,可见两次握手是不够的;三次握手已经达到建立可靠连接的目的,更多次的握手就是多次重复确认,从效率来说已无必要。
参与一条TCP连接的两个进程中的任何一个都能终止该连接。当连接结束后,主机中的”资源“(即缓存和变量)将被释放。
假设某客户打算关闭连接。客户应用进程发出一个关闭连接命令,这会引起客户TCP向服务器进程发送一个特殊的TCP报文段。这个特殊的报文段让其首部中的一个标志位即FIN比特被设置为1,当服务器接收到该报文后,就向发送方回送一个确认报文段。然后服务器发送它自己的终止报文段,其FIN比特被置为1。最后该客户对这个服务器的终止报文段进行确认。此时在两台主机上用于该连接的所有资源都被释放了
在一个TCP连接的生命周期内,运行在每台主机中的TCP协议在各种TCP状态之间变迁,下图说明了客户TCP会经历的一系列典型TCP状态。
下图展示了服务器端TCP通常要经历的一系列状态
拥塞控制方法
我们可以根据网络层是否为运输层拥塞控制提供了显示帮助来区分拥塞控制方法
- 端到端拥塞控制。在端到端拥塞控制方法中,网络层没有为运输层拥塞控制提供显示支持。即使网络中存在拥塞,端系统也必须通过对网络行为的观察来判断,TCP采用端到端的方法解决拥塞控制,因为IP层不会向端系统提供有关网络拥塞的反馈信息。
- 网络辅助的拥塞控制。在网络辅助的拥塞控制中,路由器向发送方提供关于网络中拥塞状态的显式反馈信息。这种反馈可以简单地用一个比特来指示链路中地拥塞情况。对于网络辅助的拥塞控制,拥塞信息从网络反馈到发送方通常有两种方式,如下图
直接反馈信息可以由网络路由器发给发送方;第二种形式是路由器标记或更新从发送方流向接收方的分组中的某个字段来指示拥塞的产生。
TCP拥塞控制
TCP为运行在不同主机上的两个进程之间提供了可靠传输服务,TCP的另一个关机部分就是其拥塞控制机制,TCP必须使用端到端拥塞控制而不是使用网络辅助的拥塞控制,因为IP层不向端系统提供显式的网络拥塞反馈。
TCP所采用的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率。如果一个TCP发送方感知到路径上没有拥塞,则TCP发送方增加其发送速率;反之会降低速率;但是这种方法提出三个问题
第一,一个TCP发送方如何限制它向其连接发送流量的速率呢?
第二,一个TCP发送方如何感知到它到目的地之间的路径上存在拥塞呢?
第三,当发送方感知到端到端的拥塞时,采用何种算法来改变其发送速率呢?
首先来看第一个问题,TCP发送方时如何限制向其连接发送流量的。TCP连接的每一端都是由一个接收缓存、一个发送缓存和几个变量组成。运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即拥塞窗口(congestion window)(cwnd),它对一个TCP发送方能向网络中发送流量的速率进行了限制。通过调节拥塞窗口的值,发送方因此能调整它向连接发送数据的速率。
接下来考虑TCP发送方是如何感知在它与目的地之间的路径上出现拥塞的。将一个TCP发送方的”丢包“事件定义为:要么出现超时,要么收到来自接收方的3个冗余ACK。当出现过度的拥塞时,在沿着这条路径上的一台路由器的缓存会溢出,引起一个数据报被丢弃,丢弃的数据报接着会引起发送方的丢包事件,发送方就认为在发送方到接收方的路径上出现了拥塞的指示。
TCP拥塞控制算法
该算法包括三个主要部分:①慢启动②拥塞避免③快速恢复
慢启动和拥塞避免是TCP的强制部分,两者的差异在于对收到的ACK做出反应时增加拥塞窗口长度的方式。快速恢复是推荐部分,对TCP发送方并非是必须的
慢启动
当一条TCP连接开始时,cwnd的值通常初始置为一个MSS的较小值,这就使得初始发送速率大约为MSS/RTT。对TCP发送方而言,可用带宽比MSS/RTT大得多,TCP发送方希望迅速找到可用带宽的数量,因此在慢启动状态,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加1个MSS。在下图中,TCP向网络发送第一个报文段并等待一个确认。当该确认到达时,TCP发送方将拥塞窗口增加一个MSS,并发送出两个最大长度的报文段。这两个报文段被确认,则发送方对每个确认报文段将拥塞窗口增加一个MSS,使得拥塞窗口变为4个MSS,并这样下去。这一过程每过一个RTT,发送速率就翻倍。因此TCP发送速率起始慢,但在慢启动阶段以指数增长。
何时结束这种指数增长呢?
首先如果存在一个由超时指示的丢包事件,TCP发送方将cwnd设置为1并重新开始慢启动过程。它还将第二个状态变量的值ssthresh(慢启动阈值)设置为cwnd/2。慢启动结束的第二种方式是直接与ssthresh的值相关联,当cwnd的值等于ssthresh时,结束慢启动并且TCP转移到拥塞避免模式。最后一种结束慢启动的方式是,如果检测到3个冗余ACK,这时TCP执行一种快速重传并进入快速恢复状态。
拥塞避免
一旦进入拥塞避免状态,cwnd的值大约是上次遇到拥塞时的值的一半,此时TCP采用了一种较为保守的方法,每个RTT只将cwnd的值增加一个MSS。
快速恢复
在快速恢复中,对于引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK,cwnd的值增加一个MSS。最终,当对丢失报文段的一个ACK到达时,TCP在降低cwnd后进入拥塞避免状态,如果出现超时事件,快速恢复在执行如同在慢启动和拥塞避免中相同的动作后,迁移到慢启动状态:当丢包事件出现时,cwnd的值被设置为1个MSS,并且ssthresh的值设置为cwnd值的一半。
回顾一下全局。忽略一条连接开始时初始的慢启动阶段,假定丢包由3个冗余ACK而不是超时指示,TCP的拥塞控制是:每个RTT内cwnd线性(加性)增加1个MSS,然后出现3个冗余ACK事件时cwnd减半(乘性减)。因此,TCP拥塞控制常被称为加性增、乘性减(Additive-Increase,Multiplicative-Decrease,AIMD)拥塞控制方式。
AIMD拥塞控制引发了下图中的”锯齿“行为。TCP线性地增加它的拥塞窗口长度(因此增加其运输速率),直到出现3个冗余ACK事件,然后以2个因子来减少它的拥塞窗口长度,然后又开始了线性增加,探测是否还有另外的可用带宽。