多路IO复用
目录
4、当 poll 函数解除阻塞遍历结构体数组查找出发生返回事件的下标
1、创建一个 epoll 专用的文件描述符 为接下来的 epoll 的所有操作做准备
4、对 epoll_wait 中的第二个参数指向的结构体数组进行遍历
5、出现失效的套节字需要把他从 epoll 的文件描述符中进行删除
-- 使用场景:
- 1 当程序要同时处理多个文件描述符时 需要使用多路 IO 复用
- 2 当 tcp 服务 既要监测服务器连接套节字,又同时要处理客户端通信套节字也需要使用多路 IO 复用,多路IO复用可以将多处阻塞转化为一处阻塞
-- 一共有三种方法可以实现多路 IO 复用功能
-- 使用这些方法 按部就班的来操作就可以了
三种方法实现多路IO复用
一、 select 函数
select 是最古老的 IO 多路复用机制,它使用一个文件描述符集合来监听多个 IO 事件的就绪状态。
应用程序需要将需要监听的文件描述符添加到集合中,然后调用 select 函数进行监听。
当有文件描述符就绪时,select 函数会返回,并告知哪些文件描述符已经准备好进行读取或写入操作。然后应用程序可以通过遍历文件描述符集合 来处理就绪的 IO 事件。
我们可以通过 select 函数来监测文件描述符集合中的所有文件描述符的可读性.(相当于这个文件描述符是否被生成)
- 使用 select 来进行多路 io 复用的步骤
1、创建文件描述符集合
-- 集合的数据类型为 fd_set 类型
- 例:fd_set old,new;
-- 用来保存要进行监测的文件描述符
2、对集合进行清空操作
- void FD_ZERO(fd_set *set);
-- set:填写要清空的集合的地址
3、使用函数将要进行监测的文件描述符添加到集合中
- void FD_SET(int fd, fd_set *set);
-- 函数的作用:
- 将 fd 这个文件描述符添加到集合 set 中去
-- 函数的参数:
- fd:填写要添加的文件描述符
- set:填写要添加的集合的地址
4、对集合中的文件描述符进行监测
-- 使用 select 函数来进行监测
- int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
-- 主要作用:
- 用来监测集合中的文件描述符的 IO 操作是否就绪
读操作和写操作不会被阻塞 就叫文件描述符的就绪
当监测的集合中有文件描述符就绪了
该函数会把没有就绪的文件描述符从集合中清除(所以如果要用之间加入到集合的文件描述符,就要再创建一个集合来保存)
-- 函数的参数:
- nfds:需要设置为集合中最大的文件描述符+1
- readfds:用于监测集合中所有文件描述符的可读性
会监测读操作是否就绪 - writefds:用于监测集合中所有文件描述符的可写性
- exceptfds:用于监测集合中是否有异常出现
- timeout:填写该函数的监测时常
阻塞监测为 NULL
-- 函数的返回值:
- 成功返回 集合中出现对应性质的文件描述符个数
- 失败返回 -1
5、对哪个文件描述符就绪进行判断
-- 因为 selcet 函数会把没有就绪的文件描述符从集合中清除,所以当前在集合中的文件描述符不清楚
-- 只需要判断哪一个文件描述符在集合中
- int FD_ISSET(int fd, fd_set *set)
-- 函数的作用:
- 判断文件描述符 fd 是否在集合 set 中
-- 函数的参数:
- fd:要进行判断的文件描述符
- set:要进行判断的集合
-- 函数的返回值:
- 成功返回 1
- 失败返回 0
-- 数组中存放的是客户端的文件描述符
6、当有文件描述符失效的时候
- 比如客户端下线 该套节字失效
- 我们需要把该套节字从监测的集合中清除,否则还会监测该套接字(客户端下线,read函数就不会阻塞,套接字会一直出现可读性)
- void FD_CLR(int fd, fd_set *set);
-- 函数的作用:
- 将 fd 从集合 set 中删除
-- 函数的参数:
- fd:要进行删除的文件描述符
- set:要进行操作的集合
二、poll 函数
poll 不需要每次调用都将文件描述符集合传递给内核,而是使用一个 pollfd 结构体数组来传递。
应用程序需要将需要监听的文件描述符和事件类型添加到 pollfd 数组中,然后调用 poll 函数进行监听。
当有文件描述符就绪时,poll 函数会返回,并告知哪些文件描述符已经准备好进行读取或写入操作。
然后应用程序可以通过遍历 pollfd数组来处理就绪的 IO 事件。
-- poll 轮询只需要一个函数即可实现
-
#include <poll.h>
-
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
-
函数的作用对第一个参数指向的结构体数组中的文件描述符和请求事件进行监测
-- 函数的参数:
- fds:指向 pollfd 结构体数组的指针
struct pollfd {
int fd;
/* file descriptor */ 填写要进行监测的文件描述符
short events;
/* requested events */ 请求事件 POLLIN 可读性
short revents;
/* returned events */ 实际返回的事件 如果值为 POLLIN 表示该套节字有可读性
};
- nfds:填写第一个参数指向的结构体数组的个数
- timeout:轮询时常
0
-- 运行多少毫秒
=0
-- 立即返回
-1
-- 阻塞
-- 函数的返回值:
- 成功返回 一个正数
表示该结构体中有几个套节字发生了相应的请求事件 - 超时返回 0
- 失败返回 -1
- 使用 poll 来进行多路 io 复用的步骤
1、创建结构体数组
- struct pollfd poll_fd[10] = {0};
2、将要监测的文件描述符和相应的请求事件添加到数组中去
- poll_fd[0].fd = ser;//监测的套节字
- poll_fd[0].events = POLLIN;//监测可读性
3、使用 poll 函数对结构体数组中的内容进行监测
ret = poll(poll_fd,10,-1);
4、当 poll 函数解除阻塞遍历结构体数组查找出发生返回事件的下标
for(int i =0;i<10;i++)
{
//找出来有可读性的下标
if(poll_fd[i].revents == POLLIN)
{
在这里填写要进行的操作
}
}
三、epoll 函数
epoll 是 Linux 特有的 IO 多路复用机制,它使用一个内核事件表来管理和监听多个 IO 事件的就绪状态。
应用程序需要将需要监听的文件描述符添加到内核事件表中,然后调用 epoll_wait 函数进行监听。
当有文件描述符就绪时,epoll_wait 函数会返回,并告知哪些文件描述符已经准备好进行读取或写入操作。
与 select 和 poll 不同的是,epoll 使用回调函数来处理就绪的 IO 事件,而不需要应用程序遍历事件列表。
- 使用 epoll 来进行多路 io 复用的步骤
1、创建一个 epoll 专用的文件描述符 为接下来的 epoll 的所有操作做准备
- #include <sys/epoll.h>
- int epoll_create(int size);
-- 函数作用:
- 创建一个 epoll 专用的文件描述符
-- 函数的参数:
- size 给 1 即可
-- 函数的返回值:
- 成功返回 创建的 epoll 文件描述符
- 失败返回 -1
2、将要监测套节字和相应事件添加到 epfd 中
- int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
-- 函数的作用:
- 对 epoll 专用文件描述符进行操作
- 比如:添加监测的文件描述符和相应事件
- 删除失效的文件描述符
-- 函数的参数:
- epfd:添加 epoll 专用文件描述符
- op:要进行的操作
EPOLL_CTL_ADD:进行添加操作
EPOLL_CTL_DEL:进行删除操作
使用删除操作时 第四个参数被忽略给 NULL 即可
- fd:要进行操作的套节字
struct epoll_event *event:传入参数
主要对其中的 events 进行监测事件添加
EPOLLIN 表示可读性
data.fd 赋值为要监测的套节字
-- 函数的返回值:
- 成功返回 0
- 失败返回 -1
3、对 epoll 专用的文件描述符进行轮询监测
- int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
-- 函数的作用:
- 对 epfd 中添加的所有套节字和相应事件进行轮询监测
-- 函数的参数:
- epfd:填写 epoll 专用文件描述符
- events:传出参数 填写结构体数组
该函数会把所有出现相应事件的文件描述符放到该参数指向的地址中去
所以我们只需要对第二个参数指向的地址进行相应操作即可
- maxevents:第二个参数指向的结构体数组的大小
- timeout:要轮询的时常
0 轮询多少毫秒
-1 阻塞
0 立即返回
-- 函数的返回值:
- 成功返回 同一刻有几个套节字出现相应事件
- 超时返回 0
- 失败返回 -1
4、对 epoll_wait 中的第二个参数指向的结构体数组进行遍历
- 来找到出现相应事件的文件描述符,然后进行相应的操作
5、出现失效的套节字需要把他从 epoll 的文件描述符中进行删除
使用 epoll_ctl 函数来进行删除