select/poll/epoll
1.select()函数
函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
- nfds:整型标量,比所有文件描述符集合中文件描述符最大值大1。
- readfds:此文件描述符监视文件集合中是否有数据可读,当select返回时,readfds将清除其中不可读的fd,只留下可读的fd。
- writefds:此文件描述符监视文件集合中是否有数据可写,当select返回时,writefds将清除其中不可读的fd,只留下可写的fd。
- exceptfds:此文件描述符监视文件集合中是否发生错误。
- timeout:设置在select所监视的文件集合中没有事件发生时,最长的等待时间。
fd_set结构体:
typedef struct{
long int fds_bits[32];
}fd_set;
可以理解为一个long int
类型的数组,每一位代表一个文件描述符,fd_set
最多可以表示1024个文件描述符。当调用select
时会将fd_set
拷贝到内核,内核会判断哪个fd
有数据到来,如果没有数据将会阻塞在这里;当一个或多个fd
有数据到来,会将对应的fd_set
置位并返回。fd_set不可重用。
应用场景:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = INADDR_ANY; //泛指本机ip
bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
listen(sockfd, 5);
for(i=0; i<5; i++)
{
addrlen = sizeof(client);
memset(&client, 0, addrlen);
fds[i] = accept(sockfd, (struct sockaddr*)&client, &addrlen);
if(fds[i] > max)
{
max = fds[i]; //找到最大的fd
}
}
while(1)
{
FD_ZERO(&rset); //清零rset
for(i=0; i<5; i++)
{
FD_SET(fds[i], &rset); //将fd加入rset
}
select(max+1, &rset, NULL, NULL, NULL);
for(i=0; i<5, i++)
{
if(FD_ISSET(fds[i], &rset))
{
memset(buff, 0, BUFF_LEN);
read(fds[i], buff, BUFF_LEN);
//处理数据
}
}
}
2.poll()函数
函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- fds:指向结构pollfd数组的指针,包含监视文件描述符和条件。
- nfds:需要监视描述符的个数。
- timeout:超时时间,单位为ms,当为负值时表示永远等待。
struct pollfd结构体:
struct pollfd{
int fd; //文件描述符
short events; //请求的事件
short revents; //返回的事件
}
应用场景:
for(i=0; i<5; i++)
{
addrlen = sizeof(client);
memset(&client, 0, addrlen);
pollfds[i].fd = accept(sockfd, (struct sockaddr *)&client, &addrlen);
pollfds[i].events = POLLIN; //有数据到来时 文件描述符可读
}
sleep(1);
while(1)
{
poll(pollfds, 5, 5000);
for(i=0; i<5; i++)
{
if(pollfds[i].revents & POLLIN)
{
pollfds[i].revents = 0;
memset(buff, 0, BUFF_LEN);
read(pollfds[i].fd, buff, BUFF_LEN);
//处理数据
}
}
}
3.epoll
函数原型:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event events, int maxevents, int timeout);
- epoll_create:生成一个epoll专用的文件描述符,参数指定生成描述符的最大范围。
- epoll_ctl:用于控制某个文件描述符上的事件,注册事件、修改事件、删除事件。
- epoll_wait:用于轮询I/O事件的发生。
epoll结构体:
typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64
}epoll_data_t;
struct epoll_event
{
__uint32_t events;
epoll_data_t data;
};
应用场景:
struct epoll_event events[5];
int epfd = epoll_create(10);
for(i=0; i<5; i++)
{
static struct epoll_event ev;
addrlen = sizeof(client);
memset(&client, 0, addrlen);
ev.data.fd = accept(sockfd, (struct sockaddr *)&client, &addrlen);
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev);
}
while(1)
{
nfds = epoll_wait(epfd, events, 5, 5000); //无数据阻塞 有数据返回
for(i=0; i<nfds; i++)
{
memset(buff, 0, BUFF_LEN);
read(events[i].data.fd, buff, BUFF_LEN);
//处理数据
}
}
epoll_wait
返回有数据的fd个数,只需要对数据的前nfds的元素进行处理就行了。