总结《Linux高性能服务器编程》第9章
第9章 I/O复用
- I/O复用使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要:
- 客户端程序要同时处理多个socket,或同时处理用户输入和网络连接;
- 服务器要同时监听多个端口,或者处理多种服务,或者同时处理TCP请求和UDP请求;
- TCP服务器要同时处理监听socket和连接socket;
- I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的;
- Linux下实现I/O复用的系统调用主要有select、poll和epoll;
select系统调用
-
用途:在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常等事件;
-
select API
#include<sys/select.h>
int select(int nfds, fd_set* readfds, fd_set*writefds, fd_set* exceptfds, struct timeval* timeout);
- nfds参数指定被监听的文件描述符的总数;
- readfds、writefds和exceptfds参数分别指向可读、可写和异常事件对应的文件描述符集合;
- timeout参数用来设置select函数的超时时间;
- 返回值:
- 成功时返回就绪(可读、可写和异常)文件描述符的总数;
- 超时时间内没有任何文件描述符就绪,select将返回0;
- 如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR;
- 失败时返回-1并设置errno;
-
文件描述符就绪条件
- 在网络编程中,下列情况下socket可读:
- socket内核接收缓存区中的字节数大于或等于其低水位标记SO_RCVLOWAT;
- socket通信的对方关闭连接,此时对该socket的读操作将返回0;
- 监听socket上有新的连接请求;
- socket上有未处理的错误;
- 下列情况下socket可写:
- socket内核发送缓存区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT;
- socket的写操作被关闭;
- socket使用非阻塞connect连接成功或者失败(超时)之后;
- socket上有未处理的错误;
- select能处理的异常情况:接收到带外数据;
-
处理带外数据
- socket上接收到普通数据和带外数据都将使select返回,但socket处于不同的就绪状态:
- 普通数据使socket处于可读状态;
- 带外数据使socket处于异常状态;
poll系统调用
-
在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者;
#include<poll.h>
int poll(struct pollfd* fds, nfds_t nfds, int timeout);
-
fds参数是一个pollfd结构类型的数组,指定所有用户感兴趣的文件描述符上发生的可读、可写和异常等事件;
struct pollfd
{
int fd;
short events;
short revents;
};
-
nfds参数指定被监听事件集合fds的大小;
-
timeout参数指定poll的超时值;
-
poll系统调用的返回值的含义与select相同;
epoll系列系统调用
三组I/O复用函数的比较
I/O复用的高级应用
-
非阻塞connect
- 在对非阻塞的socket调用connect,而连接又没有立即建立时,可以调用select、poll
等函数来监听这个连接失败的socket上的可写事件,函数返回后再利用getsockopt来读取错误码并清除该socket上的错误,这种非阻塞connect方式能同时发起多个连接并一起等待;
代码清单9-5 非阻塞connect
-
聊天室程序
- 使用I/O复用同时处理网络连接和用户输入;
- 客户端:从标准输入终端读入用户数据,并将用户数据发送至服务器;往标准输出终端打印服务器发送给它的数据;
- 使用poll同时监听用户输入和网络连接,利用splice函数将用户输入内容直接定向到网络连接上发送,实现数据零拷贝,提高了程序执行效率;
- 服务器:接收客户数据,并把客户数据发送给每一个登录到该服务器上的客户端;
- 使用poll同时管理监听socket和连接socket;
代码清单9-7 聊天室服务器程序
-
回射服务器
- 在实际应用中,有不少服务器程序能同时监听多个端口,比如超级服务inetd和android的调试服务adbd;
- 服务器如果要同时监听多个端口,就必须创建多个socket,并将它们分别绑定到各个端口上;
- 如果服务器要同时处理该端口上的TCP和UDP请求,则也需要创建两个不同的socket:流socket和数据报socket,并将它们都绑定到该端口上;
代码清单9-8 同时处理TCP请求和UDP请求的回射服务器
超级服务xinetd
- Linux因特网服务inetd是超级服务,它同时管理着多个子服务,即监听多个端口;
- xinetd是inetd的升级版,原理与inetd相同,但增加了一些控制选项,并提高了安全性。