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;
- }
-
相关阅读:
idea 导入项目
Python Flask框架-开发简单博客-定义和操作数据库
8/2 训练日志(dp+思维+字典树)
Mac笔记本安装maven
RISC Zero项目简介
结合原理图关于STM32后期例程的更新说明
spring之基于p命名c命名空间的注入
【UNR #6 E】神隐(交互)(二进制分组)
postgresql源码学习(51)—— 提交日志CLOG 提交日志CLOG 原理 用途 管理函数
【搜索】—— 迭代加深/双向DFS/IDA*
-
原文地址:https://blog.csdn.net/qq_55796594/article/details/127847276