套接字是通信端点的抽象。与应用程序要使用文件描述符访问文件一样,访问套接字也需要套接字描述符。套接字描述符在UNIX系统是用文件描述符实现的。事实上,许多处理文件描述符的函数(如read和write)都可以处理套接字描述符。
要创建一个套接字,可以调用socket函数。
#include <sys/socket.h>int socket(int domain, int type, int PRotocol);返回值:若成功则返回文件(套接字)描述符,若出错则返回-1
参数domain(域)确定通信的特性,包括地址格式。表16-1总结了由POSIX.1指定的各个域。各个域有自己的格式表示地址,而表示各个域的常数都以AF_开头,意指地址族(address family)。
表16-1 套接字通信域
多数系统还会定义AF_LOCAL域,这是AF_UNIX的别名。AF_UNSPEC域可以代表任何域。历史上,有些平台支持其他网络协议(如AF_IPX为NetWare协议族),但这些协议的域常数没有在POSIX.1标准中定义。
参数type确定套接字的类型,进一步确定通信特征。表16-2总结了由POSIX.1定义的套接字类型,但在实现中可以自由增加对其他类型的支持。
表16-2 套接字类型
参数protocol通常是0,表示按给定的域和套接字类型选择默认协议。当对同一域和套接字类型支持多个协议时,可以使用protocol参数选择一个特定协议。在AF_INET通信域中套接字类型SOCK_STREAM的默认协议是TCP(传输控制协议)。在AF_INET通信域中套接字类型SOCK_DGRAM的默认协议是UDP(用户数据报协议)。下表(摘自apue第3版)列出了为因特网域套接字定义的协议:
对于数据报(SOCK_DGRAM)接口,与对方通信时是不需要逻辑连接的。只需要送出一个报文,其地址是一个对方进程所使用的套接字。
因此数据报提供了一个无连接的服务。另一方面,字节流(SOCK_STREAM)要求在交换数据之前,在本地套接字和与之通信的远程套接字之间建立一个逻辑连接。
数据报是一种自包含报文。发送数据报近似于给某人邮寄信件。可以邮寄很多信,但不能保证投递的次序,并且可能有些信件丢失在路上。每封信件包含接收者的地址,使这封信件独立于所有其他信件。每封信件可能送达不同的接收者。
相比之下,使用面向连接的协议通常就像与对方打电话。首先,需要通过电话建立一个连接,连接建立好之后,彼此能双向地通信。每个连接是端到端的通信信道。会话中不包含地址信息,就像呼叫的两端存在一个点对点的虚拟连接,并且连接本身暗含特定的源和目的地。
对于SOCK_STREAM套接字,应用程序意识不到报文界限,因为套接字提供的是字节流服务。这意味着当从套接字读出数据时,它也许不会返回所有由发送者进程所写的字节数。最终可以获得发送过来的所有数据,但也许要通过若干次函数调用得到。
SOCK_SEQPACKET套接字和SOCK_STREAM套接字很类似,但从该套接字得到的是基于报文的服务而不是字节流服务。这意味着从SOCK_SEQPACKET套接字接收的数据量与对方发送的一致。流控制传输协议(Stream Control Transimission Portocol, SCTP)提供了因特网域上的顺序数据包服务。
SOCK_RAW套接字提供一个数据报接口用于直接访问下面的网络层(在因特网域中为IP)。使用这个接口时,应用程序负责构造自己的协议首部,这是因为传输协议(TCP和UDP等)被绕过了。当创建一个原始套接字时需要有超级用户特权,用以防止恶意程序绕过内建安全机制来创建报文。
调用socket与调用open相类似。在两种情况下,均可获得用于输入/输出的文件描述符。当不再需要该文件描述符时,调用close来关闭对文件或套接字的访问,并且释放该描述符以便重新使用。
虽然套接字描述符本质上是一个文件描述符,但不是所有参数为文件描述符的函数都可以接受套接字描述符。表16-3总结了到目前为止所讨论的大多数使用文件描述符的函数处理套接字描述符时的行为。未规定的和由实现定义的行为通常意味着函数不能处理套接字描述符。例如,lseek不处理套接字,因为套接字不支持文件偏移量的概念。
表16-3 使用文件描述符的函数处理套接字时的行为
套接字通信是双向的。可以采用函数shutdown来禁止套接字上的输入/输出。
#include <sys/socket.h>int shutdown(int sockfd, int how);返回值:若成功则返回0,出错则返回-1
如果how是SHUT_RD(关闭读端),那么无法从套接字读取数据;如果how是SHUT_WR(关闭写端),那么无法使用套接字发送数据;使用SHUT_RDWR则将同时无法读取和发送数据。
能够使用close关闭套接字,为何还要使用shutdown呢?理由如下:首先,close只有在最后一个活动引用被关闭时才释放网络端点。这意味着如果复制一个套接字(例如采用dup),套接字直到关闭了最后一个引用它的文件描述符之后才会被释放。而shutdown允许使一个套接字处于不活动状态,无论引用它的文件描述符数目多少。其次,有时只关闭套接字双向传输中的一个方向会很方便。例如,如果想让所通信的进程能够确定数据发送何时结束,可以关闭该套接字的写端,然而通过该套接字读端仍可以继续接收数据。
本篇博文内容摘自《UNIX环境高级编程》(第2版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。
新闻热点
疑难解答