Epoll水平触发和边缘触发的区别
(网上截图的,觉得写的挺好的)
水平触发的例子
Client端写HelloWorld给server,但是Server每次只读取5字节,所以当Client写10字节的内容给Server,第一次读取后,Server依然会有EPOLLIN的事件,然后读取剩下的5字节。
代码没有将已连接的connection保存在数组中或链表中,只是一个demo。
//server code #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/epoll.h> #include <sys/un.h> int listen_fd=-1; int epoll_fd=-1; void addEpoll(int fd){ struct epoll_event event[1]; event->events=EPOLLIN; event->data.fd=fd; epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,event); } void removeEpoll(int fd){ struct epoll_event event[1]; event->events=0; event->data.fd=fd; epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,event); } int main(void){ int ret; struct sockaddr_un addr; listen_fd =socket(AF_UNIX,SOCK_STREAM,0); if(listen_fd<0){ printf("create socket failed \n"); return -1; } memset(&addr,0,sizeof addr); addr.sun_family=AF_UNIX; strcpy(addr.sun_path,"test"); unlink(addr.sun_path); ret=bind(listen_fd,(struct sockaddr *)&addr,sizeof addr); if(ret){ printf("bind socket failed\n"); return -1; } ret=listen(listen_fd,5); if(ret){ printf("listen socket failed\n"); return -1; } epoll_fd=epoll_create(256); if(epoll_fd<0){ printf("create epoll failed\n"); return -1; } addEpoll(listen_fd); int num,i; int client_fd; char buf[20]; unsigned msec=10; struct epoll_event events[256]; while(1){ //memset zero buf memset(buf,0,20); num=epoll_wait(epoll_fd,events,256,msec); if(num>0){ for(i=0;i<num;i++){ if(events[i].data.fd==listen_fd){ printf("receive a connection\n"); client_fd=accept(listen_fd,NULL,NULL); addEpoll(client_fd); }else if(events[i].events & EPOLLHUP){ printf("receive a epollhup event\n"); removeEpoll(events[i].data.fd); }else{ read(client_fd,&buf,5); printf("%s\n",buf); } } }else{ //printf("no event\n"); } } } //client code #include <stdio.h> #include <stdlib.h> #include <sys/un.h> #include <sys/socket.h> #include <sys/types.h> int main(void){ struct sockaddr_un addr; int fd; fd=socket(AF_UNIX,SOCK_STREAM,0); if(fd<0){ printf("socket create failed \n"); } memset(&addr,0,sizeof addr); addr.sun_family= AF_UNIX; strcpy(addr.sun_path,"test"); connect(fd,(struct sockaddr*)&addr,sizeof addr); char* buf="helloWorld"; write(fd,buf,strlen(buf)); sleep(15); close(fd); }结果
边缘触发的例子
使用与水平触发的例子相同,只是将水平触发改成边缘触发。
void addEpoll(int fd){ struct epoll_event event[1]; event->events=EPOLLIN | EPOLLET; event->data.fd=fd; epoll_ctl(epoll_fd,EPOLL_CTL_ADD,fd,event); }结果
从结果图中看到,我们只读了一次,即epoll_wait只有在第一次Client 发给server的时候触发了一次EPOLLIN,即使Server没有将
缓冲区中的内容全部读完,epoll下次也不会通知我们了。
EPOLLHUP这个事件,我们明明没有注册,但是client关闭后,server依然能接受到EPOLLHUP的事件,这个东西还是需要看源码找下原因,在水平触发中,如果不处理EPOLLHUP,将fd从epoll中移除到,那么epoll会一直会报EPOLLHUP的事件。