引言
- 传输层与网络层一起构成了网络协议层次的核心。网络层使用数据报或虚电路技术为端到端通信提供了数据包交付服务。传输层架构在网络层提供的服务之上,把数据传递服务从两台计算机之间扩展到了两台计算机上的进程之间,并且服务所需的可靠性程度独立于当前使用的物理网络。传输层为应用层使用网络提供了抽象的模式。在本章我们将学习传输层,包括它的服务和API设计的选择,其中设计可靠性、连接 和拥塞控制,协议(比如TCP/UDP)和性能等问题的解决。
1、传输服务
1.1提供给上层的服务
- 传输层的最终目的是向它的用户提供高效的、可靠的和成本有效的数据传输服务,它的用户通常是应用层的进程。为了实现这个目标,传输层需要充分利用网络层提供给它的服务。在传输层内,完成这项工作的硬件和/或软件称为传输实体。传输实体可以实现在主机的不同位置,可能在操作系统内核,或者以一个链接库的形式绑定到网络应用中,或者以一个独立的用户进程运行,甚至可以实现在网络接口卡上。前两种实现方式在Internet上最常见。网络层、传输层和应用层之间的逻辑关系如图所示。
- 与网络层提供面向连接和无连接两种服务一样,传输层的服务类型也分两种。面向连接的传输服务在许多方面与面向连接的网络服务类似,两者的连接都要经历3个阶段:连接建立、数据传输和连接释放。在这两层上,寻址和流量控制非常相似。另外,无连接的传输服务与无连接网络服务也相似。
- 既然传输层服务与网络层服务如此相似,为什么还要设计两个独立的层?传输层的代码完全运行在用户的机器上,凡是网络层代码主要运行在有运营商提供的路由器上(至少对于广域网来说如此)。如果网络层提供的服务不够用,如果网络层频繁丢失数据包,如果路由器时常崩溃怎么办?
- 用于对于网络层没有真正的控制权,因为他们不拥有路由器。唯一的可能是在网络层之上再加一层,用来提高网络的服务质量。如果在一个无连接网络中,数据包被丢失或者发生错位,则传输实体可以检测到问题,并通过重传弥补错误。本质上,由于传输层的存在,使得传输服务有可能比网络服务更加可靠。而且,传输服务原语可以通过调用库程序来实现,从而使得这些原语独立于网络服务原语。不同网络上的服务原语可能有很大的差别。将网络服务隐藏在一组传输服务原语后面,如果改变了网络服务,只需要替换一组库程序即可;新的库程序使用了不同的底层网络服务,凡是实现了同样的传输服务原语。
- 正是有了传输层,程序员才可以按照一组标准原语来编写代码,并且程序可以运行在各种各样的网络上;他们无须处理不同的网络接口,也不用担心传输的可靠性。传输层承担了把上层与技术、设计和各种缺陷隔离的关键作用。基于这个原因,许多人习惯将网络分成两部分:第1层到第4层为一部分,第4层之上为一部分。下面的4层可以看做是传输服务的提供者,上面的层次可视为传输服务的用户。
1.2传输服务原语
- 为了允许用户访问传输服务,传输层必须为应用程序提供一些操作,也就是说,提供一个传输服务接口。每个传输服务都要它自己的接口。在本小节中介绍一个简单(假想的)传输服务以及相应的接口,通过它了解传输服务的本质所在。
- 传输服务类似于网络服务,但是两者之间有一些重要区别。最主要的区别是网络服务毫不掩盖地按照实际网络提供的服务来建立模型。实际网络可能会丢失数据包,所以网络服务一般来说是不可靠的。与此相反,面向连接的传输服务时可靠的。当然,实际网络并非没有错误,但是这恰好是传输层的目标——在不可靠的网络之上提供可靠的服务。- 作为一个例子,请考虑一个UNIX系统中通过管道(或者其他任何进程之间的通信设施)连接起来的两个进程。假设两者之间的连接时十分完美的——进程A把数据放进管道的一端,进程B可以从管道的另一端取出来,这是一个100%可靠的连接。这就是面向连接传输服务的真正含义所在——将网络服务的各种缺陷隐藏起来。另一方面,传输层也可以提供不可靠(数据报)服务。
- 网络服务于传输服务的第二个区别在于它们服务的对象不同。网络服务仅仅被传输实体使用。许多程序可以看到传输原语,因此传输服务的使用必须非常方便容易。
- 为了了解传输服务的基本面貌,图中列出了5个原语。
原语 | 发出的包 | 含义 |
---|---|---|
LISTEN | 无 | 阻塞,知道某个进程试图与之连接 |
CONNECT | CONNECT REQ | 主动尝试建立一个连接 |
SEND | DATA | 发送信息 |
RECEIVE | 无 | 阻塞,直到到达一个DATA包 |
DISCONNECT | DISCONNECT REQ | 请求释放连接 |
- 假设有一个应用,它有一个服务器和多个远程客户。首先,服务器执行LISTEN原语,一般的做法是调用一个库程序,由它执行阻塞该服务器的系统调用,直到有客户请求连接。当一个客户希望与该服务器通话时,它就执行CONNECT原语。传输实体执行该原语并阻塞调用方,然后给服务器发送一个包。封装在该包有效载荷中的是一条发送给服务器传输实体的传输层消息。
- 使用术语段来表示传输实体之间发送的消息。段(传输层之间的交换单元)被包裹在数据包(网络层之间的交换单元)中,而数据包则被包含在帧(数据链路层之间的单元)中。当一帧到达时,数据链路层对帧头进行处理,如果帧目标地址与本地传递地址匹配,则把帧的有效载荷字段中的内容传输给网络实体。网络实体对数据包头进行类似处理,然后把数据包的有效载荷字段向上传输给传输实体。
- 在回到客户机-服务器的例子,客户的CONNECT调用导致传输实体发送一个CONNECT REQUEST段给服务器。当该段到达服务器时,传输实体检查服务器是否阻塞在LISTEN状态。如果是,则解除服务器的阻塞,并给客户送回一个CONNECT ACCETPTED段。当该段返回客户机时,客户机的阻塞也被解除,于是连接被建立起来。现在双方可以通过SEND和RECEIVE原语交换数据。在最简单的形式中,任何一方都可以执行(阻塞的)RECEIVE原语,等待另一方执行SEND原语。当段到来时,接收端被解除阻塞;然后它可以对这个段进行处理,并发送一个应答。只有双方保持应该谁发送数据的次序,这种方案就可工作得很好。
- 在传输层上即使非常简单的单向数据交换过程也比网络层的复杂得多。发送的每个数据包(最终)都要被确认。这些确认使用网络层协议的传输实体来管理,并且它们对于传输用户是不可见的。类似的,传输实体只需要关心计时器和重传。这些机制对于传输用户全部是透明的。当不再需要一个连接时必须将它释放,以便释放两个传输实体内部的表空间。中断连接有两种方式:非对称的和对称的。在非对称的方式中,任何一方都可以发出DISCONNECT原语,从而驱使它的传输实体将一个DISCONNECT段发送给远程的传输实体。当该段到达另一端时,连接就释放了。在对称方式中,连接的两个方向是彼此独立的,因此需要单独关闭每个方向。当一方执行了DISCONNECT,这意味着它没有更多的数据发送,但是可是接收对方发过来的数据。在这种模型下,只有当双方都执行了DISCONNECT原语,一个连接才能释放。
- 图中给出了这些简单原语建立和释放连接的状态图。每个状态的迁移都是由某种事件触发的。为了简化起见,我们假设每个段单独确认;还假设采用了对称的连接释放模型,并且由客户先释放连接。请注意,这种模型非常不成熟,后面在描述TCP如何工作时会考虑更实际的模型(斜体标记的状态转移是由到达的包引起的。实现表示客户的状态序列,虚线表示服务器的状态序列)。
1.3Berkeley套接字
- 即TCP所用的套接字(socket)原语。作为Berkeley UNIX4.2 BSD软件的一部分。套件字首次发布在1983年,这些原语很快得到流行,现在已被许多操作系统应用于Internet程序设计中,尤其是基于UNIX的系统,Windows系统也有一个套接字风格的API,称为“winsock”。
- 表中列出了一些原语。这些原语遵循了我们第一个例子的模型,但提供了更多的灵活性和功能。
原语 | 含义 |
---|---|
SOCKET | 创建一个新通信端点 |
BIND | 将套接字与一个本地地址关联 |
LISTEN | 声明愿意接受连接;给出队列长度 |
ACCEPT | 被动创建一个入境连接 |
CONNECT | 主动创建一个连接 |
SEND | 通过连接发送一些数据 |
RECEIVE | 通过连接接受一些数据 |
CLOSE | 释放链接 |
- 表中列出的前4个原语按照顺序执行。SOCKET原语创建一个新的端点,并且在传输实体中为它分配相应的表空间。此调用的参数说明了采用的地址格式、所需的服务类型(比如可靠的字节流),以及所用的协议。SOCKET调用成功则返回一个普通的文件描述符,供后续的调用使用,SOCKET调用与对文件实施的OPEN调用工作方式一样。
- 新创建的套接字没有网络地址。通过BIND原语可以为套接字分配地址。一旦服务器已经将一个地址绑定到一个套接字,则远程客户就能够与它建立连接。之所以不让SOCKET调用直接创建一个地址是因为有些进程对于它们的地址比较在意,而其他进程不在乎。接下来是LISTEN调用,它为入境呼叫分配队列空间,以便在多个客户同时发起连接的段到达时,传输实体创建一个新的套接字并返回一个与其关联的文件描述符,这个新套接字与原来的套接字具有相同的属性;然后服务器可以派生一个进程或者线程来处理这个新套接字上的连接,而服务器自身又回到原来的套接字上等待下一个连接请求的到来。ACCEPT返回一个文件描述符,服务器可以按照标准的方式对它进行读写。
- 现在我们看客户端的情形。同样的,首先必须使用SOCKET原语创建一个套接字,但是客户端使用什么地址对服务器而言无所谓,所以客户端不需要调用BIND原语。CONNECT原语阻塞调用方,并主动发起建立连接的过程。当CONNECT调用完成(即收到服务器发送过来的确认段),客户进