IO多路复用

select

内容介绍

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
sockfd = socket(AF_INEF,SOCK_STREAM,0);  //服务器端监听套接字
memset(&addr, 0, sizeof(addr)); //初始化addr_in类型
addr.sin_family = AF_INET; //IPV4
addr.sin_port = hton(2000);//端口2000
addr_sin_addr.s_addr = INADDR_ANY; //

bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); //绑定陶套接字和套接口地址
listen(socfd, 5);//

for(int i = 0; i < 5; i++)
{
memset(&client,0,sizeof(client)); //初始化客户端套接字
addrlen = sizeof(client);
fds[i] = accept(sockfd, (struct sockaddr*)&client, &addrlen);
if(fds[i] > max)
max = fds[i];//记录最大的文件描述符
}

while(1)
{
fd_set rset;
FD_ZERO(&rest); //全部置为0
for(int i=0;i<5;i++)
FD_SET(fds[i],&rset); //将我们关心的文件描述符设为1

select(max+1, &rest, NULL, NULL, NULL);
// select函数会将所有的文件描述符中,有数据到来的那些描述符的对应位置置位1,其余位置置位0,
//通过for循环判断哪些文件描述符被置位,从而读取其中的数据
for(int i=0;i<5;i++)
{
if(FD_ISSET(fds[i], &rset))
{
memset(buffer,0,MAXBUF);
read(fds[i],buffer,MAXBUF); //读操作
puts(buffer);
}
}
}

使用内核态来判断那个套接字描述符有数据

缺点:有1024个文件描述符的限制;fd_set不可以重用,每次循环需要重新置位0;内核态复制文件描述符导致文件描述符列表越长需要复制的次数越多;O(n)遍历判断哪个位置的描述符被置位

poll

poll函数格式如下:

1
2
# include <poll.h>
int poll (struct pollfd * fds, unsigned int nfds, int timeout);

参数:指向pollfd结构体类型的指针,数组指针

​ 关注的文件描述符的个数

​ 时间

poolfd结构体定义如下

1
2
3
4
5
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
} ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for(int i=0;i<5;i++)
{
memset(&client,0, sizeof(client));
addrlen = sizeof(client);
pollfds[i].fd = accept(sockfd, (struct sockaddr*)&client, &addrlen);//pollfds是pollfd类型的结构体数组
pollfds[i].events = POLLIN;//关注的是有数据可读事件
}
sleep(1);
while(1)
{
poll(pollfds, 5, 50000); //调用poll函数,pollfds[i]发生了POLLIN事件, //把pollfds[i].revents对应于POLLIN置位为1
for(int i=0;i<5;i++)
{
if(pollfds[i].revents & POLLIN) //可能有多个关注事件,要与对应的事件做与操作
{
pollfds[i].revents = 0;//复位
//处理数据
}
}
}

epoll

epoll函数原理详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct epoll_event events[5];
int c = epoll_create1(10);
int epfd = epoll_create(10);//创建一个epoll对象,返回文件描述符,参数只要大于0即可
.......
.......
for(int i=0;i<5;i++)
{
static struct epoll_event ev; //临时变量
memset(&client, 0, sizeof(client));
addrlen = sizeof(client);
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, 10000);
for(i=0;i<ndfs;i++)
{
//处理数据
}
}