本篇介绍计算机网络中的运输层,内容皆总结摘抄自《计算机网络:自顶向下的方法》,仅作笔记。
运输层位于应用层和网络层之间,是分层的网络体系结构的重要部分。该层为运行在不同主机上的应用程序进程提供直接的通信服务起到至关重要的作用。
运输层服务概述
运输层协议为运行在不同的主机上的应用程序之间提供了逻辑通信(logic communication)功能。从应用程序的角度看,通过逻辑通信,即便是位于地球两端且运行不同进程的主机也好像能直接相连一样,事实上,这些主机是通过很多路由器以及多种不同类型的链路相连。应用进程使用运输层提供的逻辑通信功能彼此发送报文,而无须考虑承载这些报文的物理基础设施的细节。
在发送端,运输层将从发送应用程序进程收到的报文转换成运输层分组,该分组称为运输层报文段(segment)。该协议一种作业方式可能如下:运输层协议将应用报文划分为较小的快,并为每个块加上一个运输层首部以生成运输层报文段;在发送端系统中,运输层将这些报文段传递给网络层,网络层将其封装成网络层分组(即数据报)并向目的地发送。网络路由器仅作用域该数据报的网络层字段,即它们不检查封装在该数据报的运输层报文段的字段;在接收端系统中,网络层从数据报中提取运输层字段,并将该报文段向上交给运输层,运输层处理接收到的报文段,并将该报文段中的数据交给接收应用进程使用。
运输层和网络层的关系
在协议栈中,运输层刚好位于网络层之上。网络层提供了主机之间的逻辑通信,而运输层为运行在不同主机上的进程之间提供了逻辑通信。
举个例子,我们平常都会使用网络购物,选择好商品付款之后商家就把快递交给快递公司的一个网点,快递公司把快递运送到临近我们的菜鸟驿站,最后由菜鸟驿站分发快递。这例子是笔者想的,更符合我们的日常生活但可能类比的不咋好。在此例中,商品可以看作应用层报文,商家和买家可以看作进程,网点和菜鸟驿站可以分发的地区看作端系统,网点和菜鸟驿站看作运输层协议,快递公司看作网络层协议。在这个类比中,网点和菜鸟驿站都只负责自己的一片区域,网点从多个商家手中将商品拿过来,菜鸟驿站将商品分发出去;类似的,运输层协议只在端系统中工作,将来自应用进程的报文移动到网络边缘或从网络边缘将报文移动到应用进程。而快递公司是将一批快递从一个网点运到可能是另一个城市的菜鸟驿站,是端到端交付的过程。由此也可以看出网点和菜鸟驿站都受制于快递公司,与此类似,运输层协议能够提供的服务同样受制于底层网络层协议的服务模型。
因特网运输层概述
因特网为应用层提供了两种截然不同的可用运输层协议,一种是UDP(用户数据报协议),它为调用它的应用程序提供了一种不可靠、无连接的服务,另一种是TCP(传输控制协议),它为调用它的应用程序提供了一种可靠的、面向连接的服务。当设计一个网络应用程序时,该应用程序的开发人员必须指定使用这两种运输协议中的哪一种。因特网文献将TCP的运输层分组称为报文段,而将UDP的分组称为数据报。
在简单介绍TCP和UDP之前,先来简单了解一下因特网的网络层。因特网网络层协议的名字叫IP,即网际协议。IP为主机之间提供了逻辑通信。IP的服务模型是尽力而为的交付服务,即它尽它的最大努力在通信的主机之间交付报文段,但它并不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性。由于这些原因,IP被称为不可靠服务。另外,每台主机至少有一个网络层地址,即IP地址。
UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务。将主机间的交付扩展到进程间交付被称为运输层的多路复用与多路分解。UDP和TCP还可以通过在其报文段首部中包括差错检查字段而提供完整性检查,进程到进程的数据交付和差错检查是两种最低限度的运输层服务,也是UDP所能提供的仅有的两种服务。并且与IP一样,UDP也是一直不可靠的服务,即不能保证一个进程所发送的数据能完整无缺地到达目的地。
而TCP为应用程序提供了几种附加服务。首先,它提供可靠数据传输服务,通过使用流量控制、序号、确认和定时器,TCP确保正确地、按序地将数据从发送进程交付给接收进程。这样,TCP就将两个端系统间的不可靠IP服务转换成了一种进程间的可靠数据传输服务。TCP还提供拥塞控制服务,防止任何一条TCP连接用过多流量来淹没通信主机之间的链路和交换设备。TCP力求为每个通过一条拥塞网络链路的连接平等地共享网络链路带宽。
多路复用与多路分解
在目的主机,运输层从紧邻其下的网络层接收报文段,然后负责将这些报文段中的数据交付给在主机上运行的适当应用程序进程。当计算机运行多个网络应用进程在运行,运输层从网络层接收数据时需要将所接收的数据定向到特定的某个进程,这个过程就涉及到了多路分解。
一个进程有一个或多个套接字(socket),它相当于从网络向进程传递数据和从进程向网络传送数据的门户。因此,运输层接收到数据后并没有直接将数据交付给进程,而是交给了一个中间的套接字。由于在任一时刻,接收主机上可能不止有一个套接字,所以每个套接字都有唯一的标识符。标识符的格式取决于它是UDP还是TCP套接字。而接收主机是怎样将一个到达的运输层报文段定向到适当的套接字的呢?这是因为运输层报文段中有几个字段,接收端的运输层检查这些字段,标识出接收套接字,进而将报文段定向到该套接字。将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。在源主机从不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,然后将报文段传递到网络层,所有这些工作称为多路复用。对于之前举的网络购物的例子来说,从商家接收快递的网点做的就是多路复用操作,而将快递派送给各个买家的菜鸟驿站做的就是多路分解操作。
运输层多路复用的要求:1.套接字有唯一标识符;2.每个报文段有特殊字段来指示该报文段所要交付到的套接字。这些特殊字段是源端口号字段(source port number field)和目的端口号字段(destination port number field)。端口号是一个16比特的数,其大小在0~65535之间。0~1023范围内的端口号称为周知端口号,是受限制的,指的是它们是保留给诸如HTTP(端口号80)和FTP(端口号21)之类的周知应用层协议来使用。当我们开发一个新的应用程序时,必须为其分配一个端口号。
无连接的多路复用与多路分解
在创建UDP套接字时,运输层自动地为该套接字分配一个端口号。通常,应用程序的客户端让运输船自动地并且是透明地分配端口号,而服务器则分配一个特定的端口号。加入主机A中的一个进程具有UDP端口号23333,它要发送一个应用程序数据块给位于主机B中的另一进程,该进程具有UDP端口号12345,。主机A中的运输层创建一个运输层数据报,其中包括应用程序数据、源端口号(23333)、目的端口号(12345)和两个其他的不重要的值,然后运输层将得到的数据报交付给网络层。网络层将该数据报封装到一个IP数据报中,并尽力而为地将报文段交付给接收主机。该报文段到达主机B时,主机B的运输层检查该报文中的目的端口号并将该报文段交付给端口号12345所标识的套接字。主机B能够运行多个进程,每个进程有自己的UDP套接字及相应的端口号。当UDP报文段从网络到达时,主机B通过检查该报文段中的目的端口号,将每个报文段定向分解到相应的套接字。
一个UDP套接字是由一个二元组全面标识的,该二元组包含一个目的IP地址和一个目的端口号。如果两个UDP数据报有不同的源IP地址或源端口号,但具有相同的目的IP地址和目的端口号,那么这两个数据报将通过相同的目的套接字被定向到相同的目的进程。源端口号的作用是当主机B需要回发一个报文段给A时,B到A的数据报中的目的端口号便要从A到B的数据报中的源端口号中取值。
面向连接的多路复用与多路分解
TCP套接字与UDP套接字的差别是,TCP套接字是由一个四元组(源IP地址,源端口号,目的IP地址,目的端口号)来标识的。因此,当一个TCP报文段从网络到达一台主机时,该主机使用全部4个值来将该报文段定向分解到相应的套接字。两个TCP报文段中四个值中只要有一个值不同,都会被定向分解到不同的套接字,除非该TCP报文段携带了初试创建连接的请求。
无连接运输:UDP
UDP只是做了运输层能够做的最少工作。除了复用/分解功能以及少量的差错检测外,它几乎没有对IP增加别的东西。如果应用程序选择UDP,则该应用程序差不多就是直接与IP打交道。UDP从应用进程得到数据,附加用于多路复用/分解的源和目的端口号字段,以及两个其他小的字段,然后将该数据报交给网络层。网络层将该数据报封装到一个IP数据报中,然后尽力而为地尝试将该报文段交付给接收主机。使用UDP时,在发送报文段之前,发送方和接收方的运输层实体之间没有握手,因此,UDP被称为是无连接的。
DNS是一个通常使用UDP的应用层协议的例子。当一台主机的DNS程序想要查询一个主机地址的映射IP地址时,它构造一个DNS查询报文并将其交给UDP。无须执行任何与运行在目的端系统中的UDP实体之间的握手,主机端的UDP为此报文添加首部字段,然后将形成的报文段交给网络层。网络层将该数据报封装到一个IP数据报中,然后将其发送给一个DNS服务器。在查询主机的DNS应用程序则等待对该查询的响应,如果没收到响应,则要么试图向另一台DNS服务器发送该查询,要么通知调用的应用程序不能获得响应。
虽然TCP提供了可靠数据传输服务,而UDP不能提供,但仍有许多应用更适合用UDP,原因主要有以下几点:
- 关于发送什么数据以及何时发送的应用层控制更为精细。采用UDP时,只有应用进程将数据传递给UDP,UDP就会经此数据打包进UDP数据报并立即将其传递给网络层。而TCP拥有拥塞控制机制,以便当源和目的主机间的一条或多条链路变得极度拥塞时来遏制运输层TCP发送方,这就导致TCP的可靠交付在某些时候可能会需要很长时间。而有些实时应用通常要求最小的发送速率,不希望过分地延迟报文段的传送,且能容忍一些数据丢失,TCP服务模型就不是很适合这些应用的需要。
- 无须建立连接。TCP在开始数据传输之前要经过三次握手,而UDP缺不需要做任何准备即可进行数据传输,因此UDP不会引入建立连接的时延。
- 无连接状态。TCP需要在端系统中维护连接状态,此连接状态包括接收和发送缓存、拥塞控制参数以及序号与确认号的参数。而UDP不维护连接状态,也不跟踪这些参数。对于某些专门用于特定应用的服务器来说,使用UDP一般能支持更多的活跃客户。
- 分组首部开销小。每个TCP报文段都有20字节的首部开销,而UDP数据报仅有8字节的开销。
另外,使用UDP的应用也是可能实现可靠数据传输的,但这需要通过在应用程序自身建立可靠性机制来完成,例如通过增加确认与重传机制来实现。
UDP数据报结构
UDP数据报结构如下图:
其中应用层数据占用UDP数据报的数据字段,例如,对于DNS而言,数据字段可能是一个查询报文也可能是一个响应报文。DUP首部只有4个字段,每个字段由两个字节组成。源端口号和目的端口号用来多路分解,通过这两个字段可以将应用数据送到目的主机的相应进程上。长度字段指示了在UDP报文段中的字节数(首部字段+应用数据字段)。接收方使用检验和来检查在该数据报中是否出现了差错。
UDP检验和
UDP检验和提供了差错检测功能,即检验和用于确定当UDP数据报从源主机到目的主机移动时其中的比特是否发生了改变。检验和的值通过将发送方的UDP数据报中的所有的16比特字计算和然后再进行反码运算获得,求和时遇到的任何溢出都将被回卷。如果这个定义不好理解的话,来看一个例子。假如一个发送方的UDP数据报有下面3个比特的字:
0110 0110 0110 0000
0101 0101 0101 0101
1000 1111 0000 1100
首先将前两个比特字相加
0110 0110 0110 0000
+
0101 0101 0101 0101
=
1011 1011 1011 0101
然后将得到的比特字与第三个比特字相加
1011 1011 1011 0101
+
1000 1111 0000 1100
=
1 0100 1010 1100 0001
可以发现溢出了,出现了第17位,通过定义得知此时要”回卷“,回卷的意思是指溢出的第17位与前16位再进行一次加法运算
0100 1010 1100 0001
+
0000 0000 0000 0001
=
0100 1010 1100 0010
最后将上面计算得到的结果进行一次反码运算得到检验和1011 0101 0011 1101。接收方将数据报的三个16个比特字和检验和相加,如果最终计算的和是1111 1111 1111 1111则表示此次数据报没有差错,否则表示出现了差错。
UDP虽然提供了差错检测,但它对差错恢复无能为力。有的UDP实现直接丢弃受损的数据报,有的则是将受损的数据报交给应用程序并给出警告。
运输层还有另外几部分内容,但由于各部分内容都较为复杂,因此笔者将每部分都写了新的一篇文章,以下是运输层其他部分的文章地址,建议按照顺序阅读: