引言
UNIX系统提供的经典进程间通信机制(IPC): 管道、FIFO、消息队列、信号量以及共享存储。这些机制允许在同一台计算机上运行的进程可以相互通信。本章内容主要是研究在不同计算机上的进程相互通信的机制: 网络进程间通信(network IPC)。
套接字描述符
套接字描述符在UNIX系统上被当做一种文件描述符,UNIX系统一切皆文件。
调用socket函数,创建一个套接字
1 | #include <sys/socket.h> |
参数domain确定通信特征,包括地址格式
域 | 描述 |
---|---|
AF_INET | IPv4 |
AF_INET6 | IPv6 |
AF_UNIX | UNIX域 |
AF_UPSPEC | 未指定 |
参数type确定套接字类型,进一步确定通信特征
类型 | 描述 |
---|---|
SOCK_DGRAM | 固定长度、无连接、不可靠的报文传递 |
SOCK_RAW | IP协议的数据报接口 |
SOCK_SEQPACKET | 固定长度、有序的、可靠的、面向连接的报文传递 |
SOCK_STREAM | 有序的、可靠的、双向的、面向连接的字节流 |
参数protocal通常是0、表示为给定的域和套接字类型选择默认协议
数据报(SOCK_DGRAM)接口,类似于UDP通信,两个对等进程之间通信时不需要逻辑连接。只需要向对等进程所使用的套接字送出一个报文。
字节流(SOCK_STREAM),类似于TCP,在交换数据前,需要建立一个逻辑连接。
套接字通信时双向的。可以采用shutdown函数来禁止一个套接字的I/O。
1 | #include <sys/socket.h> |
how参数
- SHUT_RE(关闭读端),无法从套接字读取数据
- SHUT_WR(关闭写端),无法使用套接字发送数据
- SHUT_RDWR,无法读,也无法读
有两个函数用于点分十进制和二进制地址格式之前的相互转换
1 | #include <arpa/inet.h> |
将套接字与地址关联
使用bind函数来关联地址和套接字
1 | #include <sys/socket.h> |
建立连接
使用connect函数来建立连接
1 | #include <sys/socket.h> |
在connect中指定的地址是我们想与之通信的服务器地址。如果sockfd没有绑定到一个地址,connect会给调用者绑定一个默认地址。
服务器调用listen函数宣告它愿意接受连接请求
1 | #include <sys/socket.h> |
参数backlog提供了一个提示,提示系统该进程所要入队的未完成连接请求数量。实际值由系统决定
一旦队列满,系统就会拒绝多余的连接请求,所以backlog的值应该基于服务器期望负载和处理量来选择,其中处理量是指接受连接请求与启动服务的数量。
一旦服务器调用了listen,所用的套接字就能接收连接请求。使用accept函数来获得连接请求并建立连接。
1 | #include <sys/socket.h> |
函数accept返回的是套接字描述符,该描述符连接到调用connect的客户端。这个新的套接字描述符和原始套接字(sockfd)具有相同的套接字类型和地址族。传给accept的原始套接字没有关联到这个连接,而是继续保持可用状态并接收其他连接请求。
如果不关心客户端标识,可以将参赛addr和len设为NULL。否则,在调用accept之前,将addr参数设为足够大的缓冲区来存放地址,并且将len指向的整数设为这个缓冲区的字节大小。返回时,accept会在缓冲区填充客户端的地址,并且更新指向len的整数来反映该地址的大小。
如果没有连接请求等待,accept会阻塞直到一个请求到来。如果sockfd处于非阻塞模式,accept会返回-1,并将errno设置为EAGAIN或EWOULDBLOCK。
如果服务器调用accept,并且当前没有连接请求,服务器会阻塞直到一个请求到来。另外,服务器可以使用poll或select来等待一个请求的到来。在这种情况下,一个带有等待连接请求的套接字会以可读的方式出现。
数据传输
1 | #include <sys/socket.h> |
对于支持报文边界的协议,如果尝试发送单个报文的长度超过协议锁支持的最大长度,那么send会失败,并将errno设为EMSGSIZE。对于字节流协议,send会阻塞直到整个数据传输完成。函数sendto和send类似。区别在于sendto可以在无连接的套接字上指定一个目标地址。
1 | #include <sys/socket.h> |
使用recv函数接收数据
1 | #include <sys/socket.h> |
非阻塞和异步I/O
通常,recv函数没有数据可用时会阻塞等待。同样的,当套接字输出队列没有足够空间来发送消息时,send函数会阻塞。在套接字非阻塞模式下,行为会改变。在这种情况下,这些函数不会阻塞而是会失败,将errno设置为EWOULDBLOCK或者EAGAIN。当这种情况发生时,可以用poll或select来判断能否接收或者传输数据。
Single UNIX Specification 包含通用异步I/O机制的支持。套接字机制有自己的处理异步I/O的方式。
在基于套接字的异步I/O中,当从套接字中读取数据时,或者当套接字写队列中空间变得可用时,可以安排要发送的信号SIGIO。
启用异步I/O的步骤
- 建立套接字所有权,这样信号可以被传递到合适的进程。
- 在fcntl中使用F_SETOWN命令
- 在ioctl中使用FIOSETOWN命令
- 在ioctl中使用SIOCSGRP命令
- 通知套接字当I/O操作不会阻塞时发信号。
- 在fcntl中使用F_SETFL命令并且启用文件标志
O_ASYNC
。 - 在ioctl中使用FIOASYNC命令。
- 在fcntl中使用F_SETFL命令并且启用文件标志
但没有得到普遍支持