epoll() 多路复用 和 两种工作模式

1.epoll API 介绍

typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};常见的Epoll检测事件:- EPOLLIN- EPOLLOUT- EPOLLERR// 对epoll实例进行管理:添加文件描述符信息,删除信息,修改信息
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);- 参数:- epfd : epoll实例对应的文件描述符- op : 要进行什么操作EPOLL_CTL_ADD: 添加EPOLL_CTL_MOD: 修改EPOLL_CTL_DEL: 删除- fd : 要检测的文件描述符- event : 检测文件描述符什么事情// 检测函数----检测epoll树中是否有就绪的文件描述符
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);- 参数:- epfd : epoll实例对应的文件描述符- events : 传出参数,保存了发送了变化的文件描述符的信息- maxevents : 第二个参数结构体数组的大小- timeout : 阻塞时间- 0 : 不阻塞- -1 : 阻塞,直到检测到fd数据发生变化,解除阻塞- > 0 : 阻塞的时长(毫秒)- 返回值:- 成功,返回发送变化的文件描述符的个数 > 0- 失败 -1// 创建epoll实例,通过一棵红黑树管理待检测集合
int epoll_create(int size);

// epoll 的使用
// 操作步骤
// 在服务器使用 epoll 进行 IO 多路转接的操作步骤如下:1.创建监听的套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);2.设置端口复用(可选)int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));3.使用本地的IP与端口和监听的套接字进行绑定int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));4.给监听的套接字设置监听listen(lfd, 128);5.创建 epoll 实例int epfd = epoll_create(100);6.将用于监听的套接字添加到 epoll 实例中struct epoll_event ev;ev.events = EPOLLIN; //检测lfd读缓冲区是否有数据ev.data.fd = lfd;int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);接着创建一个数组,用于存储epoll_wait()返回的文件描述符struct epoll_event evs[1024];7.检测添加到epoll实例中的文件描述符是否已经就绪,并将这些已就绪的文件描述符进行处理int num = epoll_wait(epfd, evs, size, -1);① 如果监听的是文件描述符,和新客户端建立连接,将得到的文件描述符添加到epoll实例中int cfd = accept(curfd,NULL,NULL);ev.events = EPOLLIN;ev.data.fd = cfd;新得到的文件描述符添加到epoll模型中,下一轮循环的时候就可以被检测了epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);② 如果是通信的文件描述符,和对应的客户端通信,如果连接已断开,将该文件描述符从epoll实例中删除int len = recv(curfd,buf,sizeof(buf),0);if(len == 0) {// 将这个文件描述符从epoll实例中删除epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);close(curfd);}else if(len > 0) {send(curfd,buf,len,0);}8.重复第 7 步的操作

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>int main() {// 创建socketint lfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY;// 绑定int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));if(ret == -1) {perror("bind");exit(-1);}// 监听ret = listen(lfd,8);if(ret == -1) {perror("listen");exit(-1);}// 用epoll_create()创建一个epoll实例int epfd = epoll_create(100);// 将监听的文件描述符相关的检测信息添加到epoll实例中struct epoll_event epev;epev.events = EPOLLIN;epev.data.fd = lfd;epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);// 创建一个数组,用于存储epoll_wait()返回的文件描述符struct epoll_event epevs[1024];while (1) {ret = epoll_wait(epfd,epevs,1024,-1);if(ret == -1) {perror("epoll_wait");exit(-1);}printf("ret = %d\n",ret);for(int i = 0;i < ret;i++) {int curfd = epevs[i].data.fd;if(curfd == lfd) {// 监听的文件描述符有数据到达,有客户端连接struct sockaddr_in caddr;int len = sizeof(caddr);int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);// epev.events = EPOLLIN | EPOLLOUT;epev.events = EPOLLIN;epev.data.fd = cfd;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);} else {// if(epevs[i].events & EPOLLOUT) {//     continue;// }// 有数据到达,需要通信char buf[1024] = {0};int len = read(curfd,buf,sizeof(buf));if (len == -1) {perror("read");exit(-1);} else if(len == 0) {printf("client closed...\n");epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);close(curfd);} else if(len > 0) {printf("recv buf = %s\n",buf);write(curfd,buf,strlen(buf) + 1);}}}}close(lfd);close(epfd);return 0;
}

client.c

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main(int argc,char* argv[]) {int fd = socket(AF_INET,SOCK_STREAM,0);if(fd == -1) {perror("socket");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);// 连接服务器int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));if(ret == -1) {perror("connect");return -1;}int num = 0;while (1) {char sendBuf[1024] = {0};sprintf(sendBuf,"send data %d",num++);write(fd,sendBuf,strlen(sendBuf) + 1);// 接收int len = read(fd,sendBuf,sizeof(sendBuf));if(len == -1) {perror("read");return -1;}else if(len > 0) {printf("read buf = %s\n",sendBuf);}else{printf("服务器已经断开连接...\n");break;}// sleep(1);usleep(1000);}close(fd);return 0;
}

2.epoll 的两种工作模式 

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 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

【注意】 ET模式需要配合循环+非阻塞

(1)LT 模式

epoll_lt.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>int main() {// 创建socketint lfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY;// 绑定int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));if(ret == -1) {perror("bind");exit(-1);}// 监听ret = listen(lfd,8);if(ret == -1) {perror("listen");exit(-1);}// 用epoll_create()创建一个epoll实例int epfd = epoll_create(100);// 将监听的文件描述符相关的检测信息添加到epoll实例中struct epoll_event epev;epev.events = EPOLLIN;epev.data.fd = lfd;epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);// 创建一个数组,用于存储epoll_wait()返回的文件描述符struct epoll_event epevs[1024];while (1) {ret = epoll_wait(epfd,epevs,1024,-1);if(ret == -1) {perror("epoll_wait");exit(-1);}printf("ret = %d\n",ret);for(int i = 0;i < ret;i++) {int curfd = epevs[i].data.fd;if(curfd == lfd) {// 监听的文件描述符有数据到达,有客户端连接struct sockaddr_in caddr;int len = sizeof(caddr);int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);// epev.events = EPOLLIN | EPOLLOUT;epev.events = EPOLLIN;epev.data.fd = cfd;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);} else {// if(epevs[i].events & EPOLLOUT) {//     continue;// }// 有数据到达,需要通信char buf[5] = {0};int len = read(curfd,buf,sizeof(buf));if (len == -1) {perror("read");exit(-1);} else if(len == 0) {printf("client closed...\n");epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);close(curfd);} else if(len > 0) {printf("recv buf = %s\n",buf);write(curfd,buf,strlen(buf) + 1);}}}}close(lfd);close(epfd);return 0;
}

(2)ET 模式

struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};常见的Epoll检测事件:- EPOLLIN- EPOLLOUT- EPOLLERR- EPOLLET
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>int main() {// 创建socketint lfd = socket(AF_INET,SOCK_STREAM,0);struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY;// 绑定int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));if(ret == -1) {perror("bind");exit(-1);}// 监听ret = listen(lfd,8);if(ret == -1) {perror("listen");exit(-1);}// 用epoll_create()创建一个epoll实例int epfd = epoll_create(100);// 将监听的文件描述符相关的检测信息添加到epoll实例中struct epoll_event epev;epev.events = EPOLLIN;epev.data.fd = lfd;epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);// 创建一个数组,用于存储epoll_wait()返回的文件描述符struct epoll_event epevs[1024];while (1) {ret = epoll_wait(epfd,epevs,1024,-1);if(ret == -1) {perror("epoll_wait");exit(-1);}printf("ret = %d\n",ret);for(int i = 0;i < ret;i++) {int curfd = epevs[i].data.fd;if(curfd == lfd) {// 监听的文件描述符有数据到达,有客户端连接struct sockaddr_in caddr;int len = sizeof(caddr);int cfd = accept(lfd,(struct sockaddr*)&caddr,&len);// 设置cfd属性非阻塞int flag = fcntl(cfd,F_GETFL);flag |= O_NONBLOCK; fcntl(cfd,F_SETFL,flag);epev.events = EPOLLIN | EPOLLET;// 设置边沿触发epev.data.fd = cfd;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);} else {if(epevs[i].events & EPOLLOUT) {continue;}// 循环读取出所有的数据char buf[5];int len = 0;while ((len = read(curfd,buf,sizeof(buf))) > 0) {// 打印数据// printf("recv data : %s\n",buf);write(STDOUT_FILENO,buf,len);write(curfd,buf,len);}if(len == 0) {printf("client closed...\n");}else if(len == -1) {if(errno == EAGAIN) {printf("data over......\n");} else {perror("read");exit(-1);}}}}}close(lfd);close(epfd);return 0;
}

client.c

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>int main(int argc,char* argv[]) {int fd = socket(AF_INET,SOCK_STREAM,0);if(fd == -1) {perror("socket");return -1;}struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);// 连接服务器int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));if(ret == -1) {perror("connect");return -1;}int num = 0;while (1) {char sendBuf[1024] = {0};// sprintf(sendBuf,"send data %d",num++);fgets(sendBuf,sizeof(sendBuf),stdin);write(fd,sendBuf,strlen(sendBuf) + 1);// 接收int len = read(fd,sendBuf,sizeof(sendBuf));if(len == -1) {perror("read");return -1;}else if(len > 0) {printf("read buf = %s\n",sendBuf);}else{printf("服务器已经断开连接...\n");break;}// sleep(1);// usleep(1000);}close(fd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1620902.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

农村农产品信息展示网站的设计与实现(论文+源码)_kaic

摘 要 随着软件技术的迅速发展,农产品信息展示的平台越来越多,传统的农产品显示方法将被计算机图形技术取代。这种网站技术主要把农产品的描述、农产品价格、农产品图片等内容&#xff0c;通过计算机网络的开发技术&#xff0c;在互联网上进行展示&#xff0c;然后通过计算机网…

如何识别计算机病毒,怎样识别计算机病毒

电脑病毒不仅影响电脑的正常使用&#xff0c;有时候还会威胁到我们的个人信息包括财务信息的安全。下面就让学习啦小编教大家怎样识别计算机病毒吧。 识别计算机病毒的方法 病毒一般通过自我隐藏的方式来达到自己的目的&#xff0c;那么病毒一般都隐藏在系统的什么地方呢?一般…

计算机病毒为了隐藏,识别计算机病毒的方法

识别计算机病毒的方法 病毒为了能随系统启动而自启动对电脑进行危害操作&#xff0c;通常会把自己设置为自动启动。更有甚者&#xff0c;它们还会将自己注册成系统服务&#xff0c;优先于其他程序启动。下面是小编收集整理的识别计算机病毒的方法&#xff0c;欢迎阅读。 识别计…

可以查杀计算机病毒的软件,怎样彻底查杀计算机病毒

电脑中毒后很可能后导致电脑出现无法开机,卡死等各种的情况&#xff0c;那么怎样彻底查杀计算机病毒呢?学习啦小编分享了彻底查杀计算机病毒的方法&#xff0c;希望对大家有所帮助。 彻底查杀计算机病毒方法一 打开腾讯电脑管家&#xff0c;并找到杀毒页面 选择【闪电查杀】等…

检查和清除计算机病毒可以使用,如何深入检查和杀死计算机病毒

当我们的计算机感染病毒时&#xff01;如果我们想深入查杀&#xff0c;该怎么办&#xff1f;以下是学习编辑器深入检查和杀死计算机病毒的方法的详细介绍&#xff01;希望对您有帮助&#xff01; 一种深度检测计算机病毒的方法: 垃圾清除软件建议安装金山卫士&#xff0c;它可以…

2. 两数相加(中等系列)

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …

如何根据公司名称来筛选快递

在查询快递物流的时候&#xff0c;如果想要对快递公司名称一致的单号进行筛选&#xff0c;该怎么办呢&#xff1f;不知道如何操作的宝贝们&#xff0c;下面请随小编一起来试试。 需要哪些工具&#xff1f; 快递批量查询高手 快递单号若干 怎么快速查询&#xff1f; 首先&…

关于名字

为什么80%的码农都做不了架构师&#xff1f;>>> 书不尽言&#xff0c;言不尽意。言者所以在意&#xff0c;得意而忘言。先从程序里的变量名说起。作为程序员&#xff0c;我们知道&#xff0c;大部分时候&#xff0c;我们要求程序中的变量名是唯一的。比如数据库里的…

2023-2029全球与中国电子级丙二醇甲醚醋酸酯市场现状及未来发展趋

电子级丙二醇甲醚醋酸酯的定义 丙二醇甲醚醋酸酯(PMA)&#xff0c;也称丙二醇单甲醚乙酸酯&#xff0c;是一种无色、有特殊气味的高级溶剂。PMA分子中既有醚键&#xff0c;又有羰基&#xff0c;羰基又形成了酯的结构&#xff0c;同时又有烷基&#xff1b;PMA同一分子中极性与非…

2022-2028年中国低温固化粉末涂料行业市场发展调研及未来前景规划报告

报告类型:产业研究 报告格式:电子版、纸介版、电子+纸介 出品单位:智研咨询-产业信息网 智研咨询发布的《2022-2028年中国低温固化粉末涂料行业市场发展调研及未来前景规划报告》共十四章。首先介绍了低温固化粉末涂料行业市场发展环境、低温固化粉末涂料整体运行态势等,…

2022-2028年中国化工催化剂行业市场全景评估及发展趋势研究报告

报告类型&#xff1a;产业研究 报告格式&#xff1a;电子版、纸介版、电子纸介 出品单位&#xff1a;智研咨询-产业信息网 智研咨询发布的《2022-2028年中国化工催化剂行业市场全景评估及发展趋势研究报告》共十四章。首先介绍了化工催化剂行业市场发展环境、化工催化剂整体运…

2022年全球市场环氧活性稀释剂总体规模、主要生产商、主要地区、产品和应用细分研究报告

本文研究全球市场、主要地区和主要国家环氧活性稀释剂的销量、销售收入等&#xff0c;同时也重点分析全球范围内主要厂商&#xff08;品牌&#xff09;竞争态势&#xff0c;环氧活性稀释剂销量、价格、收入和市场份额等。 针对过去五年&#xff08;2017-2021&#xff09;年的历…

2021年全球强化采油表面活性剂收入大约202.3百万美元,预计2028年达到297.1百万美元

本文研究全球市场、主要地区和主要国家强化采油表面活性剂的销量、销售收入等&#xff0c;同时也重点分析全球范围内主要厂商&#xff08;品牌&#xff09;竞争态势&#xff0c;强化采油表面活性剂销量、价格、收入和市场份额等。 针对过去五年&#xff08;2017-2021&#xff0…

Modbus转Profinet网关连接三菱变频器博图快速配置

本案例将分享如何使用兴达易控的modbus转profinet网关&#xff08;XD-MDPN100&#xff09;来连接西门子1200系列plc&#xff0c;并实现三菱变频器的485通讯兼容转modbusTCP通信。通过在博图中进行配置&#xff0c;我们可以实现设备之间的连接和通信。 首先&#xff0c;我们需要…

[好书推荐] 之 <趣化计算机底层技术>

趣化计算机底层技术 底层技术优势购买 底层技术 相信很多老铁跟我一样, 在深入了解底层技术的时候 — — 就很头大 很多书籍看上去跟一个 老学究 一样, 说的话不是我们这些小白看的懂得… 看不懂就会 打击我们的自信心我们就有可能找一堆理由去玩(理所应当地去玩的那一种, 反…

基于flowplayer的视频缩略图的视频预览

前言 不得不吐槽一下咯&#xff0c;年终奖发了不到半个月的工资&#xff0c;心醉了&#xff0c;心凉了&#xff01;不过技术知识是属于自己的东西&#xff0c;有新的想法&#xff0c;学到新的知识还是的总结出来&#xff0c;和大家分享分享&#xff01; 最近一直在忙公司的项…

php myflow,WordPress安装使用Flowplayer简易指南

本文是简单易懂的现代魔法系列文章的第二弹~ 一、Flowplayer简介FlowPlayer 是一个用Flash开发的在Web上的视频播放器&#xff0c;可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。 最新版本为5.1.1&#xff0c;最新版本使用纯 HTML5 CSS3 实现的原生 VIDEO 标签&…

爬虫(bilibili热门课程记录)

什么是爬虫&#xff1f;程序蜘蛛&#xff0c;沿着互联网获取相关信息&#xff0c;收集目标信息。 一、python环境安装 1、先从Download Python | Python.org中下载最新版本的python解释器 2、再从Download PyCharm: Python IDE for Professional Developers by JetBrains中下…

web开源FlowPlayer视频播放器

1.原文地址&#xff1a;http://www.cnblogs.com/babycool/p/3172303.html 2.针对原作者的代码示例修正&#xff01; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> …

SSM+Flowplayer实现web项目网页看视频

功能描述&#xff1a; 用户在首页http://localhost:8080/SSM_HTTPS/&#xff0c;输入视频名&#xff0c;点击“走 你”按钮&#xff0c;跳转到视频页面&#xff0c;搜索符合你视频名的视频&#xff08;本项目中只有两个视频&#xff0c;test和test2&#xff0c;没有加任何控制…