1. 不精确指明的协议软件接口
在多数实现中,TCP/IP协议软件驻留在计算机的操作系统中。因此,只要应用程序使用TCP/IP通信,它就必须与操作系统交互并请求其服务。从程序员的观点看,操作系统所提供的那些例程定义了应用程序和协议软件之间的接口,即应用程序接口API(Application ProgramInterface)TCP/IP被设计成能运行在多厂商的环境之中。为了与各种不同的机器保持兼容,TCP/IP的设计者们都尽量避免使用任何一家厂商的内部数据表示。另外,TCP/IP标准还尽量避免让接口使用那些只在某一家厂商的操作系统中可用的特征,因此,TCPIP和其应用程序之间的接口是不精确指明的(loosely specified)。换言之:
TCP/IP标准没有规定应用软件与TCP/IP协议软件如何接口的细节;这些标准只建议了所需的功能集,并允许系统设计者选择有关API的具体实现细节。
1.1 优点与缺点
对协议接口使用不精确的指明,有优点也有缺点。从好的方面说,它提供了灵活性和容错能力。它允许设计者使用各种操作系统实现TCP/IP,这里的操作系统可以是个人计算机中所提供的最简单的系统,也可以是超级计算机所使用的很复杂的系统。更重要的是,它意味着设计者既可以使用过程的接口方式,也可以使用消息传递的接口方式(最适合其所用的操作系统的方式)。
从坏的方面说,不精确的指明意味着;设计者可以使得不同操作系统中接口的实现细节有所不同。当厂商增加了与现有API不同的新接口时,应用编程就会更困难,应用程序在不同机器间的移植性更差。因此,尽管系统设计者偏爱不精确指明,但应用程序员却期望有一个受限制的规范,因为这样就可使应用不用改变即可在新机器上编译。
实际上,目前只有几种可供应用程序使用TCP/IP协议的API。加利福尼亚大学伯克利分校为Berkeley UNIX操作系统定义了一种API,后来的一些系统(包括Linux)也采用了这种API,该API业已称为套接字接口(socket interface),或者套接字。Microsoft在其操作系统中采用了套接字接口,套接字API的这种变形称为Windows Socket。AT&T为其UNIX系统V(System V)定义了一种API简写为TLI。此外还定义了几种API,但都还没有获得普遍接受。
2. 接口功能
尽管TCP/P没有定义一种应用程序接口,但标准确实对接口所需要的功能提出了建议。接口必须支持如下概念性操作:
- 分配用于通信的本地资源
- 指定本地和远程通信端点
- (客户端)启动连接
- (客户端)发送数据报
- (服务器端)等待连接到来
- 发送或接收数据
- 判断数据何时到达
- 产生紧急数据
- 处理到来的紧急数据
- 从容终止连接
- 处理来自远程端点的连接终止
- 异常终止通信
- 处理错误条件或连接异常终止
- 连接结束后释放本地资源
3. 概念性接口的规约
TCPIP标准并非让实现者得不到有关API的任何帮助。它们为TCP/P指明了一个概念性接口(conceplual interface),其作用是提供示例。由于多数操作系统使用过程机制把控制权从应用程序传送给系统,所以该标准把这个概念性接口定义为一组过程和函数。该标准还提出了每个过程或函数所要求的参数以及它们所执行操作的语义。例如,TCP标准讨论了SEND过程,并列出了它的参数,应用进程为在已有的TCP连接上发送数据必须提供这些参数。
概念性操作的定义很简单:
TCP/IP标准定义的概念性接口并不指明数据的表示或编程的细节;它仅仅提供了一种可能的API的例子,操作系统可将此API提供给使用TCP/IP的应用程序。
因此,这种概念性的接口并未精确说明应用进程如何与TCP/IP进行交互。由于它没有精确描述细节,操作系统的设计者就可以自由选择其他的过程名或参数,只要它们能提供相同功能就行。
4. 系统调用
图4.1说明了系统调用机制,大多数操作系统使用这种机制在应用程序和提供服务的操作系统过程之间传递控制权。对程序员来说,系统调用无论看上去还是行为上都像是函数调用。
如图所示,当某个应用进程启动系统调用时,控制权从应用进程传递给了系统调用接口。此接口又将控制权传递给了操作系统。操作系统将此调用转给某个内部过程,该过程执行所请求的操作。内部过程一旦完成,控制权通过系统调用接口返回给应用进程,然后应用进程将继续执行。
从本质上说,只要应用程序需要从操作系统获得服务,执行这个应用程序的进程就将控制转给操作系统,执行必要的操作后就返回。由于进程要通过系统调用接口,它需要一定的特权,从而允许它读取或修改操作系统中的数据结构。然而,由于每个系统调用都会将控制转给一个由操作系统设计者所写的过程,操作系统还是要被保护的。
5. 网络通信的两种基本方法
由于操作系统的设计者们把协议软件安装在操作系统中,他们就必须选择一组过程用来访问TCP/IP协议。实现方法有两种:
- 设计者发明一组新的系统调用,应用程序用它们来访问TCP/IP。
- 设计者使用一般的I/O调用访问TCP/IP。
对第一种方法,设计者列举出所有的概念性的操作,为每个操作指定一个名字和参数,并分别把它们实现为一个系统调用。由于许多设计者认为,除非绝对必要,创建新的系统调用并不明智,所以这种方法很少采用。对第二种方法,设计者使用一般的IVO原语,但他们扩充了这些原语,使其既可用于网络协议,又可用于一般的I/O设备。当然,许多设计者选择了一种混合的方法,即尽可能地使用基本的IO功能,但对那些不能常规表达的操作则增加其他的函数。
6. LINIX中提供的基本I/O功能
为理解如何扩展一般的系统调用使其适用于TCP/IP,我们考虑一下Linux基本的I/O功能。Linux和其他UNIX操作系统提供了一组(六个)基本的系统函数,用来对设备或文件进行输人/输出操作。下图中的表列出了这些操作以及它们通常的含义。
当应用程序调用open来启动输入或输出时,系统返回一个小整数,称为文件描述符(filedescriptor),此应用程序在以后的I/O操作中会使用它。调用open带有三个参数;要打开的文件或设备的名字;一组位标志(bitflag),用来控制一些特殊的情况,例如,若文件不存在,是否要创建文件;一个访问模式,它为新创建的文件指定读/写保护。例如,代码段:
int desc
desc =open("filename",O_RDWR,0)
打开一个现有的文件flename,模式是既允许读又允许写。在获得了整数描述符desc后,应用程序在以后对这个文件的I/O操作中将使用这个标识符。例如,语句:
read(desc,buffer,128);
从文件中读取128字节的数据并写人数组buffer中。
最后,当一个应用进程结束使用一个文件时,它将调用close来撤消标识符并释放相关的资源(例如,内部缓存):
close (desc)
7. 将Linux I/O用于TCP/IP
当设计者们把TCP/IP协议加人到UNIX系统时,他们扩展了传统的UNIX I/0设施。首先,他们扩展了文件描述符集,使应用进程可以创建能被网络通信所使用的描述符。其次,他们扩展了read和write这两个系统调用,使其既可以同网络标识符一起使用,又可以同普通的文件标识符一起使用。这样,当应用进程需要通过TCP连接发送数据时,它就创建相应的标识符,并使用write传输数据。
然而,并非所有的网络通信都很容易地套用这种UNIX的open-read-write-close范例。应用进程必须指明本地的和远端的协议端口,远程IP地址,还有它将使用TCP还是UDP,以及它是否要启动传输还是要等待传人连接(这就是说,它要作为客户还是要作为服务器)。如果应用进程是服务器,它必须指明在护绝请求之前,可以接受多少传入连接请求被操作系统排队。
此外,如果应用进程选择使用UDP,它必须能传输UDP数据报,而不仅仅是字节流。套接字API的设计者提供了一些额外功能来适应这些特殊情况。在Linux中,与早期的UNIX系统一样,通过增加一些新的操作系统的系统调用来实现这些额外的功能。在下一章中我们将说明设计的细节。
8. 小结
由于TCP/P是为多厂商环境而设计的,协议标准没有精确指明应用程序使用的接口,并允许操作系统的设计者自由选择如何来实现这个接口。标准确实也讨论了概念性的接口,但它仅仅是作为一种说明性的示例。尽管这些标准把这种概念性接口定义为一组过程,但设计者可以自由选择不同的过程,或者干脆使用一种完全不同的交互风格(例如,消息传递)。
操作系统往往通过一种叫做系统调用接口的机制提供服务。当在系统中增加对TCP/IP的支持时,设计者们试图通过尽可能地扩展已有系统调用的功能,减少新增加的系统调用的数量。然而,由于网络通信所要求的一些操作不容易套用通常的I/O过程,大多数TCPIP的接口还是需要几个新的系统调用。