分类: 系统运维
2012-04-03 09:39:22
一个套接字是一个通信终端的一个抽象。正如使用文件描述符访问文件一样,应用使用套接字描述符来访问套接字。套接字描述符在UNIX系统里作为文件描述符实现。事实上,许多处理文件描述里的函数,比如read和write,都可以工作在一个套接字描述符里。
要创建一个套接字,我们调用socket函数。
domain参数决定了通信的本质,包括地址格式(下一节更深入描述)。下表总结了由POSIX.1规定的域。这些常量以AF_开头(表示address family),因为每个域都有它自己表示地址的格式。
域 | 描述 |
---|---|
AF_INET | IPv4因特网域 |
AF_INET6 | IPv6因特网域 |
AF_UNIX | UNIX域 |
AF_UNSPEC | 未指定 |
我 们在17.3节讨论UNIX域。多数系统也定义AF_LOCAL域,作为AF_UNIX的一个代名。AF_UNSPEC域是一个通配符,表示“任何”域。 历史上,一些平台提供补充网络协议的支持,比如AF_IPX表示NetWare协议族,但是这些协议的域常量没有定义在POSIX.1标准里。
type参数决定了套接字的类型,它更进一步决定了通信的特性。POSIX.1定义的套接字类型在下表中汇总,但是实现可以自由加入补充类型的支持。
类型 | 描述 |
---|---|
SOCK_DGRAM | 固定长度、无连接、不可靠的消息 |
SOCK_RAW | IP的数据报接口(POSIX.1的可选项) |
SOCK_SEQPACKET | 固定长度、序列化的、可靠的、面向连接的消息 |
SOCK_STREAM | 序列化的、可靠的、双向的、面向连接的字节流 |
protocol 参数通常为0,来为给定的域和套接字类型选择默认的协议。当多个协议为相同的域和套接字类型支持时,我们可以使用protocol参数来选择一个特定的协 议。在AF_INET域里的SOCK_STREAM的默认协议是TCP(Transmission Control Protocol,传输控制协议)。AF_INET通信域里的SOCK_DGRAM套接字的默认协议是UDP(User Datagram Protocol,用户数据报协议)。
通过一个数据报(SOCK_DGRAM)接口,在通信伙伴之间不需要逻辑连接。你所需要做的所有事是发送一个消息,地址为伙伴进程使用的套接字。
因此,一个数据报提供一个无连接的服务。另一方面,一个字节流(SOCK_STREAM),要求在你能交换数据之前,你必须在你的套接字和你想通信的伙伴所拥有的套接字之间设立一个逻辑连接。
一个数据报是自包含的消息。发送一个数据报类似于给某人邮件一封信。你可以邮寄许多信,但是你不能保证分发的顺序,而且一些可能在路上丢失。每封信包含接受者的地址,让这封信和其它的区别开来。每封信甚至可以发给不同的收件人。
相 比之下,使用一个面向连接的协议来与伙伴通信就像打电话。首先,你需要建立一个连接,通过拨打电话,但是在连接建立好后,你可以和对方双向的交流。连接是 点对点的通信渠道,基于此你可以说话。你的话语没有包含地址信息,就像一个点对点的虚拟连接存在于电话两端之间,而连接本身隐含了特殊的起点和终点。
通过SOCK_STREAM套接字,应用不知道消息的边界,因为套接字提供一个字节流服务。这意味着当我们从一个套接字读取数据时,它可能不返回和发送我们数据的进程所写的相同的字节量。我们将最终得到任何发送给我们的东西,但是它可能花费了几个函数调用。
一 个SOCK_SEQPACKET套接字就像一个SOCK_STREAM套接字,除了我们得到一个基于消息的服务而不是一个字节流服务。这表示从 SOCK_SEQPACKET套接字收到的数据量和被写的量相同。流控制传输协议(Stream Control Transmission Protocol,SCTP)在因特网域里提供一个序列化的包服务。
一个SOCK_RAW套接字提供一个数据报接口,直接到底下的网络层 (意思是因特网域里的IP)。当使用这个接口时,应用需要建立它们自己的协议头,因为传输协议(例如TCP和UDP)被绕过了。需要超级用户权限来创建一 个裸套接字来阻止恶意用户应用创建可能绕过设立好的安全机制的包。
调用socket和调用open相似。在两种情况下,你都得到一个可被I/O使用文件描述符。当你使用完这个文件描述符时,你调用close来放弃对文件或套接字的访问,并释放文件描述符以便重用。
尽 管一个套接字描述符事实上是一个文件描述符,但是你不能连同任何接受一个文件描述符参数的函数来使用一个套接字描述符。下表总结了至今我们已经描述过的能 和文件描述符一起使用的函数的大多数,并描述它们在与一个套接字描述符一起使用时会发生什么。未规定和实现定义的行为通常表示函数不能和套接字描述符一起 使用。例如,lseek不能用在套接字上,因为套接字不支持文件偏移量的概念。
函数 | 套接字上的行为 |
---|---|
close(3.3节) | 释放这个套接字 |
dup、dup2(3.12节) | 和通常一样复制这个文件描述符 |
fchdir(4.22节) | 失败,errno设为ENOTDIR |
fchmod(4.9节) | 未规定 |
fchown(4.11节) | 实现定义 |
fcntl(3.14节) | 支持一些命令,包括F_DUPFD、F_GETFD、F_GETFL、F_GETOWN、F_SETFD、F_SETTL、和F_SETOWN |
fdatasync、fsync(3.13节) | 实现定义 |
fstat(4.2节) | 一些stat结构体成员被支持,但是实现定义如何支持 |
ftruncate(4.13节) | 未规定 |
getmsg、getpmsg(14.4节) | 如果套接字用STREAMS(在Solaris上)实现可以工作 |
ioctl(3.15节) | 一些命令可工作,取决于底下的设备驱动 |
lseek(3.6节) | 实现定义的(通常失败,errno设置为ESPIPE) |
mmap(4.9节) | 未规定 |
poll(14.5.2节) | 如期望地工作 |
putmsg、putpmsg(14.4节) | 如果套接字用STREAMS(在Solaris上)实现可以工作 |
read(3.7节)和readv(14.7节) | 和recv(16.5节)等价,除了没有标志 |
select(14.5.1节) | 如期望地工作 |
write(3.8节)和writev(14.7节) | 和send(16.5节)等价,除了没有标志 |
在一个套接字上的通信是双向的。我们可以用shutdown函数来禁用一个套接字上的I/O。
如果how是SHUT_RD,那么从套接字读被禁用。如果how为SHUT_WR,那么我们不用使用这个套接字来传输文件。我们可以用SHUT_RDWR来同时禁用数据传输和接收。
既 然我们可以close一个套接字,为什么需要shudown呢?有几个原因。首先,close将会只当最后活动的引用被关闭时才释放网络终端。这表示如果 我们复制这个套接字(例如用dup),那么套接字不会被释放,直到我们关闭最后引用它的文件描述符。shutdown函数允许我们令一个套接字失效,和引 用它的活动文件描述符的数量无关。其次,有时只关闭一个方向是很方便的。例如,如果我们想我们正通信的进程可以确定我们何时完成数据传输,却仍允许我们使 用这个套接字接收这个进程发送给我们的数据,那么我们可以关闭一个套接字的写。