Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发。
LT模式是默认的工作模式,在这种模式下epoll相当于一个效率较高的poll。使用ET模式需要在内核的事件表注册文件描述符的EPOLLET事件。
编写epoll服务器的核心步骤:
- 创建绑定监听IP和端口的套接字lfd
- 创建内核事件表epfd
- 注册lfd 到内核事件表epfd
- 循环监听内核事件触发epoll_wait()
- 触发事件的套接字连接分为监听套接字和连接套接字
- 处理监听套接字(accept--->新连接注册到事件表)
- 处理连接套接字(read读数据、处理数据)
- #include
- #include
- #include
- #include
- #include
- #include
- #include "pub.h"
- #define OPENMAX 1024
-
- int main(int argc, char *argv[])
- {
- if(argc<3)
- {
- printf("usage: ./%s ip_address port_num\n", basename(argv[0]));
- return -1;
- }
- char *ip = argv[1];
- int port = atoi(argv[2]);
-
- int lfd = -1, cfd = -1, epfd = -1, nready=-1, ret = -1;
- // 绑定、监听
- lfd = socketBind(ip, port);
- if(-1 == lfd)
- return -1;
-
- epfd = epoll_create(OPENMAX);
- if(epfd < 0)
- {
- perror("epoll_create");
- return -1;
- }
-
- // 向epoll事件表注册lfd和lfd的事件(上树)
- struct epoll_event tmp_ev, client_ev[OPENMAX];
- memset(client_ev, 0, sizeof(client_ev));
-
- tmp_ev.events = EPOLLIN;
- tmp_ev.data.fd = lfd;
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tmp_ev);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
-
- char buf[1024] = "";
- // 循环处理epoll事件
- while(1)
- {
- nready = epoll_wait(epfd, client_ev, OPENMAX, -1);
- if(-1 == nready)
- {
- perror("epoll_wait");
- break;
- }
- for(int i=0; i
- {
- cfd = client_ev[i].data.fd;
- // lfd请求连接事件
- if(cfd == lfd)
- {
- cfd = Accept_with_print(lfd);
- if(-1 == cfd)
- return -1;
-
- // 注册cfd
- tmp_ev.events = EPOLLIN;
- tmp_ev.data.fd = cfd;
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp_ev);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- }
- // cfd 读取事件
- else
- {
- int num = read(cfd, buf, sizeof(buf));
- if(num < 0)
- {
- perror("read");
- return -1;
- }
- else if(num == 0)
- {
- // 先将cfd删除注册,再关闭cfd。
- ret = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- close(cfd);
- printf("client closed\n");
- }
- else
- {
- write(cfd, buf, num);
- }
- }
- }
- }
-
- // 先将lfd删除注册,再关闭lfd
- ret = epoll_ctl(epfd, EPOLL_CTL_DEL, lfd, NULL);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- close(lfd);
- close(epfd);
-
- return 0;
- }
2.边缘触发下的epoll服务器
边缘触发与水平触发服务器的核心步骤基本相似,其不同在于注册新连接事件时需要注册边缘EPOLLET模式,此外读写数据需要采用循环的方式,因此文件描述符应该设置为非阻塞模式。
编写边缘epoll服务器的核心步骤:
- 创建绑定监听IP和端口的套接字lfd
- 创建内核事件表epfd
- 注册lfd 到内核事件表epfd
- 循环监听内核事件触发epoll_wait()
- 触发事件的套接字连接分为监听套接字和连接套接字
- 处理监听套接字(accept--->新连接注册到事件表(注册EPOLLET模式))
- 处理连接套接字(循环read读数据(非阻塞)、处理数据)
epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
- #include
- #include
- #include
- #include
- #include
- #include
- #include "pub.h"
- #include
-
- #define OPENMAX 1024
- // 边缘模式ET,需要将fd设为非阻塞读取,避免循环读数据时阻塞。
- int setnonblocking(int fd)
- {
- int old_flag = fcntl(fd, F_GETFL);
- int new_flag = old_flag | O_NONBLOCK;
- fcntl(fd, F_SETFL, new_flag);
- return new_flag;
- }
- int main(int argc, char *argv[])
- {
- if(argc<3)
- {
- printf("usage: ./%s ip_address port_num\n", basename(argv[0]));
- return -1;
- }
- char *ip = argv[1];
- int port = atoi(argv[2]);
-
- int lfd = -1, cfd = -1, epfd = -1, nready=-1, ret = -1;
- // 绑定、监听
- lfd = socketBind(ip, port);
- if(-1 == lfd)
- return -1;
-
- epfd = epoll_create(OPENMAX);
- if(epfd < 0)
- {
- perror("epoll_create");
- return -1;
- }
-
- // 向epoll事件表注册lfd和lfd的事件(上树)
- struct epoll_event tmp_ev, client_ev[OPENMAX];
- memset(client_ev, 0, sizeof(client_ev));
-
- tmp_ev.events = EPOLLIN;
- tmp_ev.data.fd = lfd;
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tmp_ev);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
-
- char buf[10] = "";
- // 循环处理epoll事件
- while(1)
- {
- nready = epoll_wait(epfd, client_ev, OPENMAX, -1);
- if(-1 == nready)
- {
- perror("epoll_wait");
- break;
- }
- for(int i=0; i
- {
- cfd = client_ev[i].data.fd;
- // lfd请求连接事件
- if(cfd == lfd)
- {
- cfd = Accept_with_print(lfd);
- if(-1 == cfd)
- return -1;
- //cfd设为非阻塞
- setnonblocking(cfd);
-
- // 注册cfd 使用边沿触发的方式
- tmp_ev.events = EPOLLIN | EPOLLET;
- tmp_ev.data.fd = cfd;
- ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp_ev);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
-
- }
- // cfd 读取事件
- else
- {
- while(1)
- {
- int num = read(cfd, buf, sizeof(buf));
- if(num < 0)
- {
- /* 数据读取完毕返回值小于0,errno设置为EAGAIN或者EWOULDBLOCK */
- if((errno == EAGAIN) || (errno == EWOULDBLOCK))
- {
- break;
- }
- perror("read");
- return -1;
- }
- else if(num == 0)
- {
- // 先将cfd删除注册,再关闭cfd。
- ret = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- close(cfd);
- printf("client closed\n");
- break;
- }
- else
- {
- write(cfd, buf, num);
- }
- }
- }
- }
- }
-
- // 先将lfd删除注册,再关闭lfd
- ret = epoll_ctl(epfd, EPOLL_CTL_DEL, lfd, NULL);
- if(-1 == ret)
- {
- perror("epoll_ctl");
- return -1;
- }
- close(lfd);
- close(epfd);
- return 0;
- }
-
相关阅读:
Python基础教程:行与缩进正确用法教程
文件上传漏洞-upload靶场5-12关
局域网协议:VLAN技术介绍
链路预算仿真要求
MATLAB调用工业相机读取图像数据
LVS负载均衡群集(NAT模式、IP隧道模式、DR模式)
SpringBoot项目jar发布获取jar包所在目录路径
JAVA面经整理(8)
论一种简单的翻译系统
绝区捌--将GPT幻觉的发生率从20%以上降低到2%以下
-
原文地址:https://blog.csdn.net/qq_55796594/article/details/127847276