(上一篇地址)前面使用socket完成一个服务器对应多个客户端的小实验的时候,针对TCP连接,我们必须得创建新的进程来与新的客户端通信。那么,就意味着,1000个客户端就有有1000个server进程,这显然是不实际的。如果,我们可以提前把要监听的文件描述符放到一个集合里,一旦其中一个发生事件(不管是连上,还是通信),就去处理。这样,会方便很多。所以,今天学习一下IO复用。
1 五个I/O模型
- 阻塞I/O
- 非阻塞I/O
- I/O复用(select和poll)
- 信号驱动I/O
- 异步I/O
阻塞IO
最流行的I/O模型是阻塞I/O模型,缺省时,所有的套接口都是阻塞的。
非阻塞IO
IO复用
信号驱动IO
异步IO
2 I/O复用
如果一个或多个I/O条件满足(例如:输入已准备好被读,或者描述字可以承接更多输出的时候)我们就能够被通知到,这样的能力被称为I/O复用,是由函数select和poll支持的。
I/O复用网络应用场合
- 当客户处理多个描述字
- 一个客户同时处理多个套接口
- 如果一个tcp服务器既要处理监听套接口,又要处理连接套接口
- 如果一个服务器既要处理TCP,又要处理UDP
select
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);//从集合中删除一个描述字
int FD_ISSET(int fd, fd_set *set);//描述字是否在该集合中
void FD_SET(int fd, fd_set *set);//添加一个描述字到集合中
void FD_ZERO(fd_set *set);//清空描述字集合
- 作用:函数允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定的时间后才唤醒进程
提供了即时响应多个套接的读写事件 - 参数:
- nfds:集合中最大的文件描述符 + 1 (指定被测试的描述字个数,它的值是要被测试的最大描述字加1,描述字0、1、2…….一直到nfds均被测试)
- readfds:要检查读事件的容器
- writefds:要检查写事件的容器
- timeout:超时时间
- 返回值:返回触发套接字的个数
中间的三个参数readset、writeset和exceptset指定我们要让内核测试读、写和异常条件所需的描述字
如果我们对某个条件不感兴趣,这三个参数中相应的参数就可以设为空指针
timeout参数
时间的结构体如下:
struct timeval(
long tv_sec; //秒
long tv_usec;//微秒
);
timeout参数有三种可能
- 永远等待下去:仅在有一个描述字准备好I/O时才返回&