上一节https://www.cnblogs.com/yuanwebpage/p/12361275.html记录了多进程并发程序,除了已经描述的缺点,考虑服务器端一直在调用accept函数结束客户端请求,所以没办法进行其他响应,如响应用户的输入/输出。而多路IO复用除了能同时执行一种IO的多个操作,还能响应不同类IO的操作。
1. 基于select的IO复用
select的IO复用原理很简单。每个文件描述符在Linux系统下就是一个整数,比如现在有4个应用客户端与服务器建立连接,其套接字分别为fd0,fd1,fd2,fd3,select的原理就是将这些套接字集中起来管理,采用fd_set数组,fd_set数组每一位代表一个套接字状态,当把fd0,fd1,fd2,fd3装入fd_set时,其状态如图1.1所示
图1.1 select函数管理的fd_set数组
将某个套接字添加到fd_set数组后,将其全部置0。调用select函数时,发生事件(如fd2的套接字接收到客户端发来的消息),对应的数组位就会从0变成1,此时检测fd_set数组中从0变成1的那些位,就是发生变化的描述符。因此,使用select函数流程如下:
(1) 设置文件描述符,即声明fd_set变量;
(2) 将要监视的描述符加入fd_set数组;
(3) 将数组清零;
(4) 设置超时时间(select函数是阻塞型的,因此设置超时时间,超时还没有fd_set数组变化就返回);
(5) 调用select,监视变化,并进行后续处理。
下面看select函数的具体用法
#include <sys/select.h> int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout); maxfd:监视的文件描述符个数 readset:记录“是否存在待读取的文件描述符”的变量 writeset:记录“是否存在待写的文件描述符”的变量 exceptset:记录所有异常的文件描述符的变量, timeout:设置超时的结构体 struct timeval { long tv_sec; //秒 long tv_usec; //毫秒 }
关于fd_set的添加,清零,检测变化的函数如下:
FD_SET(int fd, fd_set* fdset); //将文件描述符fd注册到fdset中 FD_CLR(int fd, fd_set* fdset); //将文件描述符fd从fdset中删除 FD_ZERO(fd_set* fdset); //将fdset清零 FD_ISSET(int fd, fd_set* fdset); //判断fd是否发生变化,即是否有事件来临
有了以上基础知识,现在将