Epoll的工作模式
LT模式(水平模式)
假设委托内核检测读事件->检测fd的读缓冲区
读缓冲区与数据->epoll检测到了会给用户通知
a\用户不读数据,数据一直在缓冲区,epoll会一直通知
b\用户只读了一部分数据,epoll会通知
c\缓冲区的数据读完了,不通知
LT(level-triggered)是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作,如果你不作任何操作,内核还是会继续通知你。
ET模式(边沿出发)
假设委托内核检测读事件->检测fd的读缓冲区
读缓冲区有数据->epoll检测了会给用户通知
a\用户不读数据,数据一直在缓冲区,epoll下次检测的时候就不通知了
b\用户只读了一部分数据,epoll不通知
c\缓冲区的数据读完了,不通知
ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你,然后它就会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是如果一直不对这个fd做IO操作,内核不会发送更多的通知了
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死
struct epoll_event{
uint32_t events;
epoll_data_t data;
};
常见的Epoll检测事件:
-EPOLLIN
-EPOLLOUT
-EPOLLET
LT模式相关的代码
1 #include <stdio.h>
2 #include <arpa/inet.h>
3 #include <sys/epoll.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <stdlib.h>
7 int main()
8 {
9 //1、socket
10 int socketfd=socket(AF_INET,SOCK_STREAM,0);
11 if(socketfd==-1)
12 {
13 perror("socket");
14 exit(-1);
15 }
16 //2、bind绑定
17 struct sockaddr_in saddr;
18 saddr.sin_family=AF_INET;
19 saddr.sin_port=htons(9999);
20 saddr.sin_addr.s_addr=INADDR_ANY;
21 int bindfd=bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
22 //3、listen监听
23 listen(socketfd,128);
24 //调用epoll_create创建一个epoll实例
25 int epfd=epoll_create(100);
26 //将监听的文件描述符相关的检测信息添加到epoll实例中
27 struct epoll_event epev;
28 epev.events=EPOLLIN;
29 epev.data.fd=socketfd;
30 epoll_ctl(epfd,EPOLL_CTL_ADD,socketfd,&epev);//对epoll实例进行管理,添加文件描述符信息
31 struct epoll_event epevs[1024];
32 while(1)
33 {
34 int ret=epoll_wait(epfd,epevs,1024,-1);//检测函数
35 //返回发送变化的文件描述符的个数
36 if(ret==-1)
37 {
38 perror("epoll_wait");
39 exit(-1);
40 }
41 printf("ret=%d\n",ret);
42 for(int i=0;i<ret;i++)
43 {
44 int curfd=epevs[i].data.fd;
45 if(curfd == socketfd)
46 {
47 //监听的文件描述符有数据到达,有客户端连接
48 struct sockaddr_in cliaddr;
49 int len=sizeof(cliaddr);
50 int cfd=accept(socketfd,(struct sockaddr *)&cliaddr,&len);
51
52 epev.events=EPOLLIN;
53 epev.data.fd=cfd;
54 epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
55
56 }
57 else
58 {
59 char buf[5]={0};
60 int len=read(curfd,buf,sizeof(buf));
61 if(len==-1)
62 {
63 perror("read");
64 exit(-1);
65 }
66 else if(len==0)
67 {
68 printf("client closed...\n");
69 epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
70 close(curfd);
71 }
72 else if(len>0)
73 {
74 printf("read buf=%s\n",buf);
75 write(curfd,buf,strlen(buf)+1);
76 }
77 }
78 }
79
80
81
82
83 }
84
85 close(socketfd);
86 close(epfd);
87 return 0;
88 }
ET模式相关的代码
1 #include <stdio.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <arpa/inet.h>
5 #include <sys/epoll.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <stdlib.h>
9 int main()
10 {
11 //1、socket
12 int socketfd=socket(AF_INET,SOCK_STREAM,0);
13 if(socketfd==-1)
14 {
15 perror("socket");
16 exit(-1);
17 }
18 //2、bind绑定
19 struct sockaddr_in saddr;
20 saddr.sin_family=AF_INET;
21 saddr.sin_port=htons(9999);
22 saddr.sin_addr.s_addr=INADDR_ANY;
23 int bindfd=bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr));
24 //3、listen监听
25 listen(socketfd,128);
26 //调用epoll_create创建一个epoll实例
27 int epfd=epoll_create(100);
28 //将监听的文件描述符相关的检测信息添加到epoll实例中
29 struct epoll_event epev;
30 epev.events=EPOLLIN;
31 epev.data.fd=socketfd;
32 epoll_ctl(epfd,EPOLL_CTL_ADD,socketfd,&epev);//对epoll实例进行管理,添加文件描述符信息
33 struct epoll_event epevs[1024];
34 while(1)
35 {
36 int ret=epoll_wait(epfd,epevs,1024,-1);//检测函数
37 //返回发送变化的文件描述符的个数
38 if(ret==-1)
39 {
40 perror("epoll_wait");
41 exit(-1);
42 }
43 printf("ret=%d\n",ret);
44 for(int i=0;i<ret;i++)
45 {
46 int curfd=epevs[i].data.fd;
47 if(curfd == socketfd)
48 {
49 //监听的文件描述符有数据到达,有客户端连接
50 struct sockaddr_in cliaddr;
51 int len=sizeof(cliaddr);
52 int cfd=accept(socketfd,(struct sockaddr *)&cliaddr,&len);
53 //设置cfd属性非阻塞
54 int flag=fcntl(cfd,F_GETFL);
55 flag | O_NONBLOCK;
56 fcntl(cfd,F_SETFL,flag);
57
58 epev.events=EPOLLIN | EPOLLET;//设置边沿出发
59 epev.data.fd=cfd;
60 epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
61
62 }
63 else
64 {
65 if(epevs[i].events & EPOLLOUT)
66 {
67 continue;
68 }
69
70
71 //循环读取所有数据
72 char buf[5]={0};
73 int len=0;
74 while((len=read(curfd,&buf,sizeof(buf)))>0)
75 {
76 // printf("recv:%s\n",buf);
77 write(STDOUT_FILENO,buf,len);
78 write(curfd,buf,len);
79 }
80 if(len==-1)
81 {
82 if(errno==EAGAIN)
83 {
84 printf("data over...");
85 }
86 else
87 {
88 perror("read");
89 exit(-1);
90 }
91 }
92 else if(len==0)
93 {
94 printf("client closed...\n");
95 }
96 }
97 }
98
99
100
101
102 }
103
104 close(socketfd);
105 close(epfd);
106 return 0;
107 }