传输层:(1)传输服务原语和Berkeley套接字
????传输层与网络层一起构成了网络协议层的核心。网络层使用数据包或虚电路技术作为端到端通信提供了数据包交付服务。传输层架构在网络层提供的服务之上,把数据传递服务从两台计算机之间 扩展到两台计算机上的进程之间 ,并且服务所需的可靠性程度独立于当前使用的物理网络。
????面向连接的传输服务在许多方面与面向连接的网络服务类似,两者的连接都要经过3个阶段:连接建立、数据传输和连接释放。于是,一个很显然的问题 出现了:既然传输层服务与网络层服务如此相似,为什么还要设立两个独立的层呢?问题的答案有些微妙,但非常关键。传输层的代码完全运行在用户的机器上,但是网络层代码主要运行在由运营商操作的路由器上(至少对于广域网是如此)。用户对网络层没有真正的控制权 ,因为他们不用有路由器,所以不能用更好的路由器或者在数据链路层上用更好的错误处理机制来解决服务太差的问题。唯一的可能是在网络层之上再加一层,由该层来提高网络的服务质量 。
1.传输服务原语
传输服务和网络服务有两个重要区别 :
- 网络服务毫不掩盖地按照实际网络提供的服务来建立模型。实际网络可能会丢失数据包,所以
网络服务一般来说是不可靠的 。与此相反,面向连接的传输服务是可靠的。当然,实际网络并非没有错误,但是,这恰好是传输层的目标—在不可靠的网络之上提供可靠的服务 - 网络服务和传输服务之间的服务对象不同。网络服务仅仅被传输实体(硬件)使用。相反,许多程序和程序员可以看到传输原语。
为了了解传输服务的基本面貌,下图列出5个原语。
连接过程:
????为了清楚这些原语的可能用法,请考虑一个应用,它有一个服务器和多个远程客户。首先,服务器执行LISTEN 原语,一般的做法是调用一个库过程,由它执行阻塞该服务器的系统调用,直到有客户请求连接。当一个客户希望与该服务器进行通话时,它就执行CONNECT 原语。传输实体执行该原语并阻塞调用方,然后给服务器发送一个包。封装在该包有效载荷中的是一条发送给服务器传输实体的传输层消息。
????客户的CONNECT调用导致传输实体发送一个CONNECTION REQUEST 段给服务器。当该段到达服务器时,传输实体检查服务器是否阻塞在LISTEN 状态(即服务器对处理请求感兴趣)。如果是,则解除服务器的阻塞,并给客户返回一个CONNECTION ACCEPTED 段。当该段返回到客户机时,客户机的阻塞也被解除,于是连接被建立了起来。
????现在双方可以通过SEND 和RECEIVE 原语交换数据。在最简单的形式中,任何一方都可以执行(阻塞的)RECEIVE 原语,等待另一方执行SEND 原语。当段到来时,接收端被解除阻塞;然后它可以对这个段进行处理,并发送一个应答。只要双方保持应该谁发送数据的次序,这种方案就可以工作得很好。
????对传输用户而言,连接就是一个可靠的比特管道:一个用户在管道一端将比特塞进去,这些比特就会神奇地出现在管道的另一端。
????当不再需要一个连接时必须将它释放,以便释放两个传输实体内部的表空间。中断连接有两种方式:非对称的和对称的。在非对称方式中,任何一方都可以发出DISCONNECT 原语,从而驱使它的传输实体将一个DISCONNECT 段发送给远程的传输实体。当该段到达另一端时,连接就被释放了。
????在对称方式中,连接的两个方向彼此独立,因此需要单独关闭每个方向。当一方执行了DISCONNECT ,这意味着它没有更多数据需要发送,但是它仍然愿意接受对方发送过来的数据。在这种模式下,只有当双方都执行了DISCONNECT 原语,一个连接才算真正被释放。
2.Berkeley套接字
????现在让我们简要地考察另一组传输原语,即TCP所用的套接字(Socket)原语 。
????图6-5列出了一些原语。粗略的说,这些原语遵循我们第一个例子的模型,但提供了更多的功能和灵活性。
服务器:
????表中列出的前4个原语由服务器按照顺序执行。SOCKET 原语创建一个新的端点(endpoint),并且在传递实体中为它分配相应的表空间。此调用的参数说明了采用的地址格式、所需的服务类型(比如可靠的字节流),以及所用的协议。SOCKET调用成功则返回一个普通的文件描述符,供后续的调用使用,SOCKET调用与对文件实施的OPEN调用工作方式一样。
????新近创建的套接字没有网络地址。通过BIND 原语可以为套接字分配地址。一旦服务器已经将一个地址绑定到一个套接字,则远程客户就能够与它建立连接。之所以不让SOCKET调用直接创建一个地址是因为有些进程对于它们的地址比较在意(比如,它们多年来一直使用同样的地址,所以每个人都知道他们的地址),而其他的进程并不在乎。
????接下来是LISTEN 调用,它为入境呼叫分配队列空间,以便在多个客户同时发起连接请求时,将这些入境的连接请求排入队列依次处理。与我们第一个例子中的LISTEN不同的是,套接字模型中的LISTEN并不是一个阻塞调用 。
????为了阻塞 自己以便等待入境连接的到来,服务器执行ACCEPT 原语。当一个请求连接的段到达时,传输实体创建一个新的套接字并返回一个与其关联的文件描述符,这个新套接字与原来的套接字具有相同的属性;然后服务器可以派生一个进程或者线程来处理这个新套接字上的连接,而服务器自身又回到原来的套接字上等待下一个连接请求的到来。ACCEPT返回一个文件描述符,服务器可以按照标准的方式对它进行读或者写操作,就像访问文件一样。
客户端:
????现在我们来看客户端的情形。同样地,这里首先也必须使用SOCKET 原语创建一个套接字,但是因为客户端使用什么地址对服务器而言无所谓,所以客户端不需要 调用BIND原语。CONNECT 原语阻塞调用方,并主动发起建立连接过程。当CONNECT调用完成(即接收到服务器发送过来的确认段),客户进程被解除阻塞,于是连接就建立起来了。现在双方都可以使用SEND 或者RECV ,在新建的全双工连接上发送或者接收数据。如果SEND和RECV调用不要求特殊选项的话,服务器或者客户也可以使用标准的UNIX系统调用READ和WRITE。
连接释放:
在套接字模型中,连接的释放是对称的。当双方都执行了CLOSE 原语之后,连接就被释放了。
|