这里有个epoll的反应堆模式实现,在这段代码之前,不知道什么是reactor反应堆,赶紧去补了一下Reator模式 论文阅读整理,这个论文是以面向对象的思想来介绍反应堆的实现的。

对比这个epoll的反应堆实现,发生跟论文中的实现还是有点出入,而普通的epoll实现epoll 示例跟不用reator模式差不多,当然思想上还是有差别的。

在反应堆实现中,为每个fd由自己封装了一个myevent_s结构体,这个结构体与fd是一一对应的,另外还封装了一个回调函数,这个回调函数就是这个fd感兴趣的事件发生时调用的函数,在上面的论文中,这个回调函数称为Concrete Event Handler,因为是个函数,所以就跟抽象处理器Event Handler没什么关系了,如下:

/** status:1表示在监听事件中,0表示不在* last_active:记录最后一次响应时间,做超时处理*/
struct myevent_s {int fd;                 //cfd listenfdint events;             //EPOLLIN  EPLLOUTvoid *arg;              //指向自己结构体指针void (*call_back)(int fd, int events, void *arg);int status;char buf[BUFLEN];int len;long last_active;
};

与普通epoll编程不同的是,使用reactor模式不需要使用Switch 语句或者if语句来判断fd或者对应事件,而是直接调用与这个fd相关联的事件处理函数,主要处理如下:

     /* 等待事件发生 */int nfd = epoll_wait(g_efd, events, MAX_EVENTS + 1, 1000);if (nfd < 0) {printf("epoll_wait error, exit\n");break;}for (i = 0; i < nfd; i++) {//强制转换ptr指针为自定义封装事件类型struct myevent_s *ev = (struct myevent_s *) events[i].data.ptr;//事件派发,回调函数保存在由ptr指针指向的自定义事件类型中的回调函数中if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {ev->call_back(ev->fd, events[i].events, ev->arg);}if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {ev->call_back(ev->fd, events[i].events, ev->arg);}}

程序完整代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#define MAX_EVENTS  1024
#define BUFLEN 128
#define SERV_PORT   8080/** status:1表示在监听事件中,0表示不在* last_active:记录最后一次响应时间,做超时处理*/
struct myevent_s {int fd;                 //cfd listenfdint events;             //EPOLLIN  EPLLOUTvoid *arg;              //指向自己结构体指针void (*call_back)(int fd, int events, void *arg);int status;char buf[BUFLEN];int len;long last_active;
};int g_efd; /* epoll_create返回的句柄 */
struct myevent_s g_events[MAX_EVENTS + 1]; /* +1 最后一个用于 listen fd *//** 封装一个自定义事件,包括fd,这个fd的回调函数,还有一个额外的参数项* 注意:在封装这个事件的时候,为这个事件指明了回调函数,一般来说,一个fd只对一个特定的事件* 感兴趣,当这个事件发生的时候,就调用这个回调函数*/
void eventset(struct myevent_s *ev, int fd, void (*call_back)(int, int, void *),void *arg) {ev->fd = fd;ev->call_back = call_back;ev->events = 0;ev->arg = arg;ev->status = 0;//memset(ev->buf, 0, sizeof(ev->buf));//ev->len = 0;ev->last_active = time(NULL);return;
}//声明回调函数,好在定义之前引用
void recvdata(int fd, int events, void *arg);
void senddata(int fd, int events, void *arg);/** 这个函数封装了epoll_ctl函数,将一个自定义事件(自己封装的事件)添加到epoll实例中* 中间那个events表示感兴趣的事件*/
void eventadd(int efd, int events, struct myevent_s *ev) {struct epoll_event epv = { 0, { 0 } };int op;//epoll_event的data数据域,用来保存用户数据,这里将一个指针ptr指向自定义的事件类型//在epoll_event中,这个指针是可以指向任何类型的 void*//epoll_event与一个fd是一一对应的epv.data.ptr = ev;epv.events = ev->events = events;if (ev->status == 1) {op = EPOLL_CTL_MOD;} else {op = EPOLL_CTL_ADD;ev->status = 1;}if (epoll_ctl(efd, op, ev->fd, &epv) < 0)printf("event add failed [fd=%d], events[%d]\n", ev->fd, events);elseprintf("event add OK [fd=%d], op=%d, events[%0X]\n", ev->fd, op,events);return;
}/*从epoll实例中删除一个事件 */
void eventdel(int efd, struct myevent_s *ev) {struct epoll_event epv = { 0, { 0 } };if (ev->status != 1)return;epv.data.ptr = ev;ev->status = 0;epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv);return;
}/*看名字好像是接受一个新的连接*/
void acceptconn(int lfd, int events, void *arg) {struct sockaddr_in cin;socklen_t len = sizeof(cin);int cfd, i;if ((cfd = accept(lfd, (struct sockaddr *) &cin, &len)) == -1) {if (errno != EAGAIN && errno != EINTR) {/* 暂时不做出错处理 */}printf("%s: accept, %s\n", __func__, strerror(errno));return;}do {for (i = 0; i < MAX_EVENTS; i++) {if (g_events[i].status == 0)break;}if (i == MAX_EVENTS) {printf("%s: max connect limit[%d]\n", __func__, MAX_EVENTS);break;}int flag = 0;if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) {printf("%s: fcntl nonblocking failed, %s\n", __func__,strerror(errno));break;}//下面这两句表示封装一个自定义的事件,以及将这个事件添加到epoll实例中eventset(&g_events[i], cfd, recvdata, &g_events[i]);eventadd(g_efd, EPOLLIN, &g_events[i]);} while (0);printf("new connect [%s:%d][time:%ld], pos[%d]\n", inet_ntoa(cin.sin_addr),ntohs(cin.sin_port), g_events[i].last_active, i);return;
}/**接收数据*/
void recvdata(int fd, int events, void *arg) {struct myevent_s *ev = (struct myevent_s *) arg;int len;len = recv(fd, ev->buf, sizeof(ev->buf), 0);eventdel(g_efd, ev);if (len > 0) {ev->len = len;ev->buf[len] = '\0';printf("C[%d]:%s\n", fd, ev->buf);/* 转换为发送事件 */eventset(ev, fd, senddata, ev);eventadd(g_efd, EPOLLOUT, ev);} else if (len == 0) {close(ev->fd);/* ev-g_events 地址相减得到偏移元素位置 */printf("[fd=%d] pos[%d], closed\n", fd, (int) (ev - g_events));} else {close(ev->fd);printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));}return;
}void senddata(int fd, int events, void *arg) {struct myevent_s *ev = (struct myevent_s *) arg;int len;len = send(fd, ev->buf, ev->len, 0);//printf("fd=%d\tev->buf=%s\ttev->len=%d\n", fd, ev->buf, ev->len);//printf("send len = %d\n", len);eventdel(g_efd, ev);if (len > 0) {printf("send[fd=%d], [%d]%s\n", fd, len, ev->buf);eventset(ev, fd, recvdata, ev);eventadd(g_efd, EPOLLIN, ev);} else {close(ev->fd);printf("send[fd=%d] error %s\n", fd, strerror(errno));}return;
}void initlistensocket(int efd, short port) {int lfd = socket(AF_INET, SOCK_STREAM, 0);fcntl(lfd, F_SETFL, O_NONBLOCK);eventset(&g_events[MAX_EVENTS], lfd, acceptconn, &g_events[MAX_EVENTS]);eventadd(efd, EPOLLIN, &g_events[MAX_EVENTS]);struct sockaddr_in sin;memset(&sin, 0, sizeof(sin));sin.sin_family = AF_INET;sin.sin_addr.s_addr = INADDR_ANY;sin.sin_port = htons(port);bind(lfd, (struct sockaddr *) &sin, sizeof(sin));listen(lfd, 20);return;
}int main(int argc, char *argv[]) {unsigned short port = SERV_PORT;if (argc == 2)port = atoi(argv[1]);g_efd = epoll_create(MAX_EVENTS + 1);if (g_efd <= 0)printf("create efd in %s err %s\n", __func__, strerror(errno));initlistensocket(g_efd, port);/* 事件循环,缓冲区 */struct epoll_event events[MAX_EVENTS + 1];printf("server running:port[%d]\n", port);int checkpos = 0, i;while (1) {/* 超时验证,每次测试100个链接,不测试listenfd 当客户端60秒内没有和服务器通信,则关闭此客户端链接 */long now = time(NULL);for (i = 0; i < 100; i++, checkpos++) {if (checkpos == MAX_EVENTS)checkpos = 0;if (g_events[checkpos].status != 1)continue;long duration = now - g_events[checkpos].last_active;if (duration >= 60) {close(g_events[checkpos].fd);printf("[fd=%d] timeout\n", g_events[checkpos].fd);eventdel(g_efd, &g_events[checkpos]);}}/* 等待事件发生 */int nfd = epoll_wait(g_efd, events, MAX_EVENTS + 1, 1000);if (nfd < 0) {printf("epoll_wait error, exit\n");break;}for (i = 0; i < nfd; i++) {//强制转换ptr指针为自定义封装事件类型struct myevent_s *ev = (struct myevent_s *) events[i].data.ptr;//事件派发,回调函数保存在由ptr指针指向的自定义事件类型中的回调函数中if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {ev->call_back(ev->fd, events[i].events, ev->arg);}if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {ev->call_back(ev->fd, events[i].events, ev->arg);}}}/* 退出前释放所有资源 */return 0;
}

epoll的反应堆实现模式相关推荐

  1. epoll 主从反应堆模式代码实现

    epoll 主从反应堆模式 学习极客时间<网络编程>. 将 epoll 主从反应堆模式 部分的代码单独剥离出来,并写了简单的makefile.方便大家学习. 代码地址:epoll_serv ...

  2. epoll服务器反应堆模型

    常规的epoll处理 epoll是io多路复用的一种实现方式,最开始我们使用epoll是对多个fd进行管理,当epoll_wait从内核的rdllist就绪链表中取出一定数量的poll_event时, ...

  3. epoll的两种模式

    From: http://haoningabc.iteye.com/blog/1432958 linux异步IO浅析  http://hi.baidu.com/_kouu/blog/item/e225 ...

  4. epoll的ET工作模式和LT工作模式

    1.epoll的两种工作模式介绍 epoll的两种模式ET和LT: LT模式(水平触发):是缺省的工作方式,并且同时支持block和non-block socket.在这种做法中,内核告诉你一个文件描 ...

  5. epoll 边沿触发(ET 模式)和水平触发(LT 模式)

    (1)应用 ET 模式的目的:改变 epoll_wait 的默认属性,可以减少调用 epoll_wait 函数的调用次数. (2)思想由来:模拟电路的高低电频的思想. 水平触发: 持续的 1 持续的 ...

  6. 【Linux网络编程】epoll进阶之水平模式和边沿模式

    ------------->[Linux系统编程/网络编程](学习目录汇总) <-------------- 文章目录 1.epoll的事件模型 1.1 ET(边沿模式)的设置 1.2 基 ...

  7. Epoll在LT和ET模式下的读写方式

    在一个非阻塞的socket上调用read/write函数,返回EAGAIN 或者 EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK) 从字面上看, 意思是:EAGAIN: 再试一次 ...

  8. epoll的ET和LT模式

    从man手册中,得到ET和LT的具体描述如下 EPOLL事件有两种模型: Edge Triggered (ET) Level Triggered (LT) 假如有这样一个例子: 1. 我们已经把一个用 ...

  9. epoll的LT和ET模式

    原理參考该博客 从man手冊中,得到ET和LT的详细描写叙述例如以下 EPOLL事件有两种模型: Edge Triggered (ET) Level Triggered (LT) 假如有这样一个样例: ...

最新文章

  1. 销售流程管理-leangoo
  2. 手把手教你玩转ARP包(一)
  3. junit4 集成测试_使用JUnit规则进行干净的集成测试
  4. POJ-3154-Graveyard
  5. JS判断文本框中只能输入数字和小数点
  6. linux testlink安装,Linux下安装testlink
  7. 一步步学习SPD2010--第十四章节--在Web页面使用控件(2)--使用标准ASP.NET服务器控件...
  8. android实战技巧,实战技巧:Android异步指南
  9. 一个迅速崛起的国产开源OCR项目!
  10. 6 DHCPV6原理
  11. 组策略锁定计算机时间,Win7电脑怎么禁止修改系统时间?两种禁止方法全教给你!...
  12. 二、概率p值检验例题(R语言)
  13. Learning Git Branching 题解(基础、高级、Git远程仓库)
  14. 文件下载文件中文名问题
  15. python字符类型是英文_Python中常见数据类型
  16. 十字军之王3等待服务器响应,王国风云3作弊码有什么 十字军之王3控制台代码分享_游侠网...
  17. ckeditor5 添加ckeditor5-line-height-plugin插件
  18. win10分屏快捷键无法使用_Win10 1909碰上多任务如何快速四分屏 Win10 1909分屏快捷键无效怎么办...
  19. mysql mmm 扩展_Mysql - 高可用方案之MMM(一)
  20. 【愚公系列】2022年10月 微信小程序-电商项目-微信支付小程序确认支付结果和退款功能实现(node)

热门文章

  1. 程式中檢查是否潤年的新方法
  2. 微软Surface Pro笔记本如何设置u盘启动教程
  3. 单片机 stm32 差分升级 增量升级算法源码,纯c编写跨平因为是程序源码
  4. 蓝奏批量自定义域名替换源码
  5. split()方法的使用
  6. var_dump()的用法
  7. MFC界面控件BCGControlBar v33.3 - 可视化管理器和主题更新
  8. 要做股市赢家:杨百万读书笔记
  9. 思考如何概括“技术美术”(Technical Artist)的职责
  10. leetcode系列-844.比较含退格的字符串