epoll使用详解
所用到的数据结构
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_event 被用于注册所感兴趣的事件和回传所发生待处理的事件,
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;
1、epoll_create函数
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数: epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修
改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
3、epoll_wait函数
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值;
返回发生事件数。
在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:
#define __FD_SETSIZE 1024
表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。
epoll的接口非常简单,一共就三个函数:
1. int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
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 */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
4、关于ET、LT两种工作模式:
可以得出这样的结论:
ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.
那么究竟如何来使用epoll呢?其实非常简单。
通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。
首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则范围。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。
epoll_wait范围之后应该是一个循环,遍利所有的事件。
几乎所有的epoll程序都使用下面的框架:
for( ; ; )
{
nfds = epoll_wait(epfd,events,20,500);
for(i=0;i<nfds;++i)
{
if(events[i].data.fd==listenfd) //有新的连接
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept这个连接
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //将新的fd添加到epoll的监听队列中
}
else if( events[i].events&EPOLLIN ) //接收到数据,读socket
{
n = read(sockfd, line, MAXLINE)) < 0 //读
ev.data.ptr = md; //md为自定义类型,添加数据
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓
}
else if(events[i].events&EPOLLOUT) //有数据待发送,写socket
{
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取数据
sockfd = md->fd;
send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //发送数据
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据
}
else
{
//其他的处理
}
}
}
下面给出一个完整的服务器端例子:
|
客户端直接连接到这个服务器就好了。。
相关文章:

软件测试(一)
最近的时间内,我印象最深刻的Bug是在上学期的javaweb的大作业中。 其中的要求是在工作人员的每一条记录后面添加一个修改按钮,要求把前一个页面的内容带入到下一个页面中,由于密码采用的是MD5的加密,所以带入到后面的页面中的内容…

网络分流器-网络分流器IP网络路由交换测试技术探讨
网络分流器1 . 与流量相关的L2-3层高级测试技术探讨戎腾网络分流器: 对于一个L2-3层网络设备,最基本、最重要的测试是流量转发性能测试。作为一个网络转发设备,首先要保证可以高速、低时延、稳定地转发流量。相关的性能测试通常是通过流量生成器…

浅谈https\ssl\数字证书
在互联网安全通信方式上,目前用的最多的就是https配合ssl和数字证书来保证传输和认证安全了。本文追本溯源围绕这个模式谈一谈。 名词解释 首先解释一下上面的几个名词: https:在http(超文本传输协议)基础上提出的一种安全的http协议…

input不管用 vue_Vue自定义指令实现快速读取Excel
前几天因为业务需求,所维护的而后台中出现了大量关于上传下载Excel的操作。因为我们的后台是基于Vue,并且是在 vue-element-admin 的基础上结合实际需求开发而来。vue-element-admin 中也有一些相关操作 Excel 的示例,都十分清晰明了…

数据结构——算法之(010)( 字符串的左旋转操作)
【申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出。 联系邮箱:Mr_chenping163.com】 题目:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转2位得到字符串cde…

value_counts()
Serise类型: Series.value_counts(normalizeFalse, sortTrue, ascendingFalse, binsNone, dropnaTrue) 功能:返回包含唯一值计数的对象。结果对象将按降序排列,以便第一个元素是最常出现的元素。 不包括默认的NA值。 参数:normali…

DFA和NFA
1.历史:引用正则表达式萌芽于1940年代的神经生理学研究,由著名数学家Stephen Kleene第一个正式描述。具体地说,Kleene归纳了前述的神经生理学研究,在一篇题为《正则集代数》的论文中定义了“正则集”,并在其上定义了一…

adc采样的值跳动_嵌入式er必知:模数采样知多少(最全总结)
[导读] 生活环境周围信号万万千,对于一个嵌入式er。我们利用技术去了解世界、改变世界。而一个产品要与外界物理环境打交道,一个至关重要的触角就是采样真实模拟世界的信号,翻译成芯片可理解的数字信号,进而实现很多为人服务的应…

Swift泛型
泛型是为Swift编程灵活性的一种语法,在函数、枚举、结构体、类中都得到充分的应用,它的引入可以起到占位符的作用,当类型暂时不确定的,只有等到调用函数时才能确定具体类型的时候可以引入泛型。 泛型函数 定义 fun 函数名<T,S&…

02、在层级未知情况下通过递归查找子物体
1、在在层级未知情况下通过递归查找子物体 ,这个主要是用于UI的的层级查找中 2、代码: 1 using System.Collections;2 using System.Collections.Generic;3 using UnityEngine;4 5 public class EnemyManager : MonoBehaviour6 {7 8 private GameOb…

CentOS装机必备-基本设置以及缺失文件
SecureCRT中注意不要使用以Ascii方式上传文件,只有在需要的地方才使用。主要是虚拟机中安装CentOS每次总会做一些设置,记录下来方便以后。 纯粹基本设置,比如本地SecureCRT可以连接虚拟机中的CentOS。 复杂的非基本设置见:Linux …

unity替换mesh测试
直接替换SkinnedMeshRender的Mesh,实现所谓断肢效果(不过最近发现,绑定多mesh似乎更好实现这样的效果。有时间准备写一篇): 只要不改变两个Mesh原始文件的层级,就不会出现权重的错乱问题。 权重映射的测试:http://www.…

matlab中patch命令_matlab 放大平移图形是超出边界问题的处理
matlab提供的图形放大和平移函数zoom和pan可以通过鼠标来控制图形,非常方便,在工具条toolbar上也有对应的按钮。但是在放大或平移自己画的数据图是,有时会出现部分图形超出了坐标系的边界的问题,非常奇怪。经分析和试验࿰…

关于虚拟化技术软硬件兼容问题的探讨
VMware十几年前就已经出现,个人最早使用VMware的时间似乎是2001年或者2002年,当时可以在个人电脑上通过VMware虚拟多套系统,用于学习研究(做实验往往会破坏系统,当时VMware在一些场景下还是比较流行的)。由…

自己开发操作系统
算是《30天自制操作系统》的读书笔记吧,但是我觉得原书不少地方啰嗦,某些做法值得商榷 http://product.china-pub.com/36828381.二进制编译器 首先下载Bzl1621.lzh,这个可以把二进制数编辑的软件。 BZ启动画面打开img文件2.使用虚拟机加载IMG…

广东科技学院专插本c语言考卷_广东科技学院第二届红色文化节之红色影视经典配音大赛决赛...
红色经典影视配音大赛追忆革命岁月,传承红色文化,激扬青春生命,传承红色精神,为了让广大师生感受到红色影视经典的魅力和配音的乐趣,加深对红色文化的理解,提高师生们的爱国情怀。2020年12月16日19…

Social regularizations
trust-aware :如何从隐式信任中导出显示信任。链接预测就是搞这一方面的么? 和类似谱聚类的拉普拉斯矩阵结合在一起,没怎么看。

阿里P7架构师的成长之路
前言 系统架构师是近几年来在国内外迅速成长并发展良好的一个职位,它的重要性及给互联网行业所带来的影响是不言而喻的。很多程序员把成为一名优秀的架构师作为自己职业生涯奋斗的目标,但很多人努力却用不对地方,前段时间我与在阿里的P7架构师…

cad的文字嵌入线条_带你玩转CAD!
CAD画图已经成为化工人的必备技能。什么,这么多CAD必备技巧你居然还不知道?我该拿什么拯救你,我最最最最最最亲爱的旁友!!!下面给大家整理了50个相见恨晚的CAD技巧,带你玩转CAD!&…

BZOJ1315 : Ural1557Network Attack
找到一棵dfs搜索树,给每条非树边一个随机非0权值,每条树边为所有经过它的树边的权值的异或。 那么有2种情况是合法的: 1.一条边权值为0,一条边权值非0。 2.两条边异或和为0。 排序后统计即可,时间复杂度$O(m\log m)$。…

android原生跳转到外网
2019独角兽企业重金招聘Python工程师标准>>> super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent new Intent(); intent.setAction("android.intent.action.VIEW"); Uri uri Uri.parse("…

linux上使用strace查看C语言级别的php源码【一种方法】
如果你希望看到C语言级别的php代码就需要使用strace 这个默认是安装了的,如果没有安装可以 #yum install strace查看httpd进程 #ps auxw | grep httpd有多个,必须停止apache [rootlocalhost usr]# /usr/local/webserver/apache2/bin/apachectl stop启动…

iphone8p百度云认证_探秘百度数据工厂Pingo的多存储后端数据联合查询技术
作者介绍:张志宏,2013年加入百度大数据部,曾作为核心成员参与百度大数据平台的搭建。目前是百度数据工厂Pingo核心团队的技术负责人。Pingo是来自百度的离线大数据集成开发平台,使用Spark作为计算引擎,深度整合了资源调…

JavaScript文件中调用AngularJS内部方法或改变$scope变量
需要在其他JavaScript文件中调用AngularJS内部方法或改变$scope变量,同时还要保持双向数据绑定; 首先获取AngularJS application: 方法一:通过controller来获取app var appElement document.querySelector([ng-controllermainCon…

web类协议脚本-飞机订票系统示例
以下是LR自带的飞机订票系统的Demo,希望能帮助大家。 Action() {int iRand;int iTmp;char *strTmpA;char *strTmpB;char *strTmpC;char *position;if ((strTmpA (char *)malloc(100 * sizeof(char))) NULL) { lr_output_message ("Insufficient memory avail…

ACCEPT()和ACCEPT4()
ACCEPT章节:Linux 程序员手册 (2) 更新:2010-09-10到 易美翻译 翻译名字accept - 通过套接口接受一个连接 概要 #include Esys/types.h> /* 参看 “注意小节” */ #include Esys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen…

没有提示_华为手机发出莫名的提示音,打开什么也没有?原来是它们在作怪
不知道你们有没有遇到过这样的情况,在使用手机的过程中会出现一个非常奇怪的现象:当你听到手机发出声音,打开手机却发现什么通知也没有?这一度让我感到很困扰,本着“打破砂锅问到底”的精神,终于让我找到了…

近段时间佛我就偶尔无
jo建瓯市金佛玩手机欧力紧凑度 我株型紧凑我阿九倨傲四局李嘉诚转载于:https://juejin.im/post/5b8e5263e51d4538e2278c9b

php内核探索方法与资源
PHP内核探索 TIPI深入理解PHP内核 风雪之隅PHP源码分析 《php扩展开发及内核应用》 百度XLQ Gods blog codinglabsPHP内核探索:从SAPI接口开始PHP内核探索:一次请求的开始与结束PHP内核探索:一次请求生命周期PHP内核探索:单进程SA…

feign调用走不走网关全局拦截_feign服务端出异常客户端处理的方法
在使用feign进行远程方法调用时,如果远程服务端方法出现异常,客户端有时需要捕获,并且把异常信息返回给前端,而如果在开启熔断之后,这个异常会被消化,所以说,如果希望拿到服务端异常,…