三种IO复用类型
- Select系统调用
#include<sys/select.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds,struct timeval* timeout);
#nfds表示监听的文件描述符总数;
#readfds,writefds,execptfds分别表示对应的fd_set类型的集合
可以如下定义:fd_set readfds,writefds,execptfds
#timeout表示select函数的超时时间
Struct timeval
{
Long tv_sec;
Long tv_usec;
}
如果timeval成员变量均为0,则select立即返回;如果timeout设置为NULL,则select将一直阻塞,直到某个文件描述符就绪。
# FD_ZERO(fd_set *fdset);清楚fdset的所有位,如FD_ZERO(&readfds);
#FD_SET(int fd,fd_set* fdset);设置fdset的位,也就是将某个文件描述符加入到fdset中,如FD_SET(0,&readfds),将标准输入加入到fdset中
#int FD_ISSET(int fd,fd_set * fdset);测试fdset的某个位是否被设置,也就是测试文件描述符中的fd是否有事件发生
#select成功时返回就绪(可读,可写和异常事件)文件
2. Poll系统调用
#include<poll.h>
int poll(struct pollfd* fds,nfds_t nfds,int timeout);
#fds是pollfd类型的数组
Struct pollfd
{
int fd;//文件描述符
int events;//注册该文件描述符要监听的事件
short revents;//由内核修改,判断该文件描述符实际发生的事件
}
#nfds表示文件描述符数组的大小
#timeout指定poll的超时值;当timeout位-1时,poll调用将永远阻塞;当timeout为0时,poll调用将立即返回。
例:
pollfd fds[2];//pollfd的数组
fds[0].fd=0;//注册标注输入
fds[0].events=POLLIN;//监听输入事件
fds[0].revents=0;//初始化
fds[1].fd=sockfd;//注册网络socket文件描述符
fds[1].events=POLLIN | POLLRFHUP;//监听
fds[1].revents=0;
int ret=poll(fds,2,-1);
#poll调用成功时返回就绪(可读,可写和异常事件)文件
3. Epoll系统调用
#include<sys/epoll.h>
int epoll_create(int size);
#epoll把文件描述符放在内核的一个时间表中
#改函数注册内核事件表需要创建多大size
#返回值作为内核事件表的索引
#include<sys/epoll.h>
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout)
#在一段时间内timeout等待一组文件描述符上的事件
#epfd表示上面注册的内核事件表的索引
#events用于存储内核事件表中就绪的事件,将内核事件表中就绪的事件复制到events中,events只用来输出epoll_wait检测到的就绪事件
#maxevents指定epoll_wait最大监听多少个事件
#timeout
#返回值表示就绪的文件描述符的个数,且就绪的文件描述符就存储于events中,直接遍历events[0,ret-1]
# struct epoll_event
{
_uint32_t events;
//表示注册的文件描述符索要监听的事件,与pollfd结构体中events类似(E)
epoll_data_t data;
}
#typedef union epoll_data//联合体
{
void *ptr;
int fd;//最常用fd,指向目标文件描述符
unint32_t u32;
uint64 u64;
}
#include<sys/epoll.h>
int epoll_ctl(int epfd,int op,struct epoll_event *event);//操作内核事件表
- op选项:EPOLL_CTL_ADD,往事件表中注册fd事件
EPOLL_CTL_MOD,修改fd上的注册事件
EPOLL_CTL_DEL,删除fd上注册的事件
event表示对内核事件表进行操作的具体情况
#向内核事件表中注册事件
void addfd(int epollfd,int fd)//epollfd表示内核事件表索引fd表示事件描述符
{
epoll_event event;
event.events=EPOLLIN;
event.data.fd=fd;//将文件描述符添加到内核事件表中
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
}
例:
epoll_event events[MAX_EVENT_NUMBER];
int epollfd=epoll_create(5);//注册大小为5的内核事件表
assert(epollfd!=-1);
addfd(epollfd,int fd);//将文件描述符fd添加到内核事件表
while(1)
{
int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1)
for(int i=0;i<number;i++)
{
//遍历监听到的事件
int sockfd=events[i].data.fd;
if(sockfd==listenfd)
//对于socket,如果是监听端口有事件到来,则创建新的连接
{
struct sockaddr_in client_address;
socklen_t client_addrlength=sizeof(client_address);
int connfd=accept(listenfd,(struct sockaddr*) &client_address,&client_addrlength) ;//从监听端口中取出事件
addfd(epollfd,connfd);//将事件注册到内核事件表中
}
else if(events[i].events & EPOLLIN)//表示客户端有数据发送到服务端
{
//接收客户端的数据
}
}
}