reactor 模型

主要框架:
当有事件发生时,调用对应的回调函数(数据接受/发送,处理);

  1. reactor 会一直跑 run,即一直处在 epoll_wait 中等待事件的到来;
  2. 当有事件到来,会调用相应事件的回调函数(之前的事件注册);
  • 以下例子利用reactor模型实现接受客户端消息,并将消息发回给客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>#define BUFFER_LENGTH      1024
#define MAX_EPOLL_EVENTS   1024typedef int CALLBACK(int, int, void *);int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);struct ntyevent {int fd;int events;void *arg;int (*callback)(int fd, int events, void *arg);char buffer[BUFFER_LENGTH];int length;int status;
};struct ntyreactor {int epfd;struct ntyevent *events;
};void nty_event_set(struct ntyevent *ev, int fd, int events, CALLBACK *callback, void *arg)
{ev->fd = fd;ev->events = events;ev->arg = arg;ev->callback = callback;
}int nty_event_add(int epfd, struct ntyevent *ev)
{struct epoll_event ep_ev = {0, {0}};int op;ep_ev.data.ptr = ev;ep_ev.events = ev->events;if (ev->status == 1){op = EPOLL_CTL_MOD;}else{op = EPOLL_CTL_ADD;ev->status = 1;}if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0){perror("epoll_ctl:add");return -1;}return 0;
}int nty_event_del(int epfd, struct ntyevent *ev)
{struct epoll_event ep_ev = {0, {0}};if (ev->status != 1){return -1;}ep_ev.data.ptr = ev;ev->status = 0;epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev);return 0;
}int ntyreactor_destroy(struct ntyreactor *reactor)
{close(reactor->epfd);if (reactor->events != NULL){free(reactor->events);reactor->events = NULL;}return 0;
}int ntyreactor_init(struct ntyreactor *reactor)
{if (reactor == NULL){return -1;}memset(reactor, 0, sizeof(struct ntyreactor));reactor->epfd = epoll_create(1);if (reactor->epfd <= 0){perror("epoll create");return -2;}reactor->events = (struct ntyevent*)malloc(MAX_EPOLL_EVENTS * sizeof(struct ntyevent));if (reactor->events == NULL){close(reactor->epfd);return -3;}return 0;
}int ntyreactor_run(int epfd, struct ntyreactor *reactor)
{int nready;struct epoll_event events[MAX_EPOLL_EVENTS] = {0};int i;while (1){nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, -1);if (nready < 0){perror("epoll_wait");continue;}for (i = 0; i < nready; i++){struct ntyevent *ev;ev = (struct ntyevent *)events[i].data.ptr;if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)){ev->callback(ev->fd, ev->events, ev->arg);}if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)){ev->callback(ev->fd, ev->events, ev->arg);}}}return 0;
}int send_cb(int fd, int events, void *arg)
{struct ntyreactor *reactor = (struct ntyreactor *)arg;struct ntyevent *ev = &reactor->events[fd];int len;len = send(fd, ev->buffer, sizeof(ev->buffer), 0);nty_event_del(reactor->epfd, ev);if (len < 0){printf("send to [%d] error\n", fd);}else{nty_event_set(ev, fd, EPOLLIN, recv_cb, reactor);nty_event_add(reactor->epfd, ev);}return len;
}int recv_cb(int fd, int events, void *arg)
{struct ntyreactor *reactor = (struct ntyreactor *)arg;struct ntyevent *ev = &reactor->events[fd];int len;memset(ev->buffer, 0, sizeof(ev->buffer));len = recv(fd, ev->buffer, sizeof(ev->buffer), 0);nty_event_del(reactor->epfd, ev);if (len < 0){printf("[%d]recv error\n", fd);close(fd);}else if (len == 0){close(fd);printf("[%d]close\n", fd);}else{printf("[%d]recv:%s\n", fd, ev->buffer);nty_event_set(ev, fd, EPOLLOUT, send_cb, reactor);nty_event_add(reactor->epfd, ev);}return len;
}int accept_cb(int fd, int events, void *arg)
{struct ntyreactor *reactor;struct sockaddr_in client_addr;int sin_size;int cfd;reactor = (struct ntyreactor *)arg;if (reactor == NULL) return -1;memset(&client_addr, 0, sizeof(client_addr));sin_size = sizeof(client_addr);cfd = accept(fd, (struct sockaddr *)&client_addr, &sin_size);if (cfd < 0){perror("accept");return -1;}printf("Client IP:%s\n", inet_ntoa(client_addr.sin_addr));//    int flag = 0;
//  if ((flag = fcntl(cfd, F_SETFL, O_NONBLOCK)) < 0) {//      printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
//      return -2;
//  }nty_event_set(&reactor->events[cfd], cfd, EPOLLIN, recv_cb, reactor);nty_event_add(reactor->epfd, &reactor->events[cfd]);return 0;
}int ntyreactor_addlistener(int sockfd, struct ntyreactor *reactor, CALLBACK *acceptor)
{if (reactor == NULL) return -1;if(reactor->events == NULL) return -2;nty_event_set(&reactor->events[sockfd], sockfd, EPOLLIN, acceptor, reactor);nty_event_add(reactor->epfd, &reactor->events[sockfd]);return 0;
}int init_sock(int port)
{int sfd;struct sockaddr_in my_addr;sfd = socket(PF_INET, SOCK_STREAM, 0);if (sfd < 0){perror("socket");return -1;}memset(&my_addr, 0, sizeof(my_addr));my_addr.sin_family = AF_INET;my_addr.sin_port = htons(port);my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr("192.168.240.160");if (bind(sfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0){perror("bind");return -1;}if (listen(sfd, 20) < 0) {perror("listen");}return sfd;
}int main(void)
{struct ntyreactor reactor = {0, 0};int port = 8000;int sockfd = init_sock(port);ntyreactor_init(&reactor);ntyreactor_addlistener(sockfd, &reactor, accept_cb);ntyreactor_run(reactor.epfd, &reactor);ntyreactor_destroy(&reactor);close(sockfd);return 0;
}
  1. 当有客户端连接时,调用一开始注册的accept_cb();
  2. accept后,将读事件注册(含相应的处理回调函数)进reactor里,实现监听相应客户端的读事件;
  3. 相应客户端的事件发生,调用对应的回调函数(2中注册的);
  4. 在本例中,服务器读取完数据后,将读事件注册进reactor里,等待可写事件发生;

reactor 相关源码学习

  • libevent 源码分析

reactor 模型相关推荐

  1. 为什么说Netty是性能之王,因为它用了 Reactor 模型啊

    点击关注公众号,Java干货及时送达 本文将介绍基于进程/线程模型,服务器如何处理请求.值得说明的是,具体选择线程还是进程,更多是与平台及编程语言相关. 例如 C 语言使用线程和进程都可以(例如 Ng ...

  2. Redis之单线程 Reactor 模型

    纯内存访问,所有数据都在内存中,所有的运算都是内存级别的运算,内存响应时间的时间为纳秒级别.因此 redis 进程的 cpu 基本不存在磁盘 I/O 等待时间.内存读写性能问题,CPU 不是 redi ...

  3. libevent源码学习-----Reactor模型

    libevent内部采用了reactor模型 所谓reactor模型,其实就是一套事件注册机制,用来解决单线程的阻塞问题.reactor核心思想是将事件和相应事件发生时想要调用的函数都记录下来,在事件 ...

  4. 五分钟快速理解 Reactor 模型

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 本文将介绍基于进 ...

  5. 模型描述的关系模式_框架篇:见识一下linux高性能网络IO+Reactor模型

    前言 网络I/O,可以理解为网络上的数据流.通常我们会基于socket与远端建立一条TCP或者UDP通道,然后进行读写.单个socket时,使用一个线程即可高效处理:然而如果是10K个socket连接 ...

  6. 多线程reactor模型

    reactor 多线程的实现最大的区别是拥有一个专门用来处理实际I/O 操作是线程池 优点: 1.拥有一个Acceptor 专门用来监听请求的I/O 类型 2.使用专门线程池可以提高acceptor的 ...

  7. Reactor模型-单线程版

    Reactor模型是典型的事件驱动模型.在网络编程中,所谓的事件当然就是read.write.bind.connect.close等这些动作了.Reactor模型的实现有很多种,下面介绍最基本的三种: ...

  8. IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。

    1.什么是BIO,NIO,AIO JAVA BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程并处理,如果这个连接不做任何事情会造成不必要的开销,当然 ...

  9. [C/C++后端开发学习] 7 tcp服务器的epoll实现以及Reactor模型

    tcp服务器的epoll实现以及Reactor模型 1 IO多路复用 select poll epoll 2 epoll详解 2.1 基本使用方法 2.2 LT水平触发和ET边沿触发 2.3 实现服务 ...

  10. Linux 高性能服务器开发笔记:Reactor 模型定时器 | 网络编程定时器

    本文主要根据游双书本 Linux 高性能服务器开发 学习分析 linux 网络编程常用到的定时器模型,配备详细理解和分析,同时分析了 Linux 内核中定时器的低精度时间轮和高精度定时器实现思路还有 ...

最新文章

  1. MFC程序框架的剖析
  2. 移动端网站优化有哪些技巧性问题?
  3. Source Insight编辑器配置
  4. asp编程工具_使用ASP.NET Core构建RESTful API的技术指南
  5. java 转换url中文参数
  6. go修改服务器时间,Windows 配置时间同步服务器以及配置时间同步间隔
  7. 【原创】Extjs4 通用CURD方法
  8. matlab2c使用c++实现matlab函数系列教程-conv函数
  9. 在线工具:找到神器,助你轻松应对各种职场难题
  10. 人工智能学习——模糊控制
  11. 如何破解锐捷支持多网卡
  12. 英雄联盟自定义局怎么和其他服务器的玩家玩,英雄联盟自定义为什么没有玩家进来...
  13. installshield 如何实现Oracle数据库脚本的执行功能
  14. 计算机中mac ip地址查询,如何通过mac地址查ip,详细教您Mac怎么查看ip地址
  15. x86 单线并发多拨_带宽“单线多拨“倍增大法教程
  16. Writeup For WeChall
  17. 视觉技术中的图像采集卡
  18. Java线程同步-模拟买票
  19. 基于堆栈二值化自动编码器和二值化神经的无约束人脸表情识别算法(An efficient unconstrained FERa based on BAEs and BNN)
  20. Redis 使用场景

热门文章

  1. linux程序测试工具gprof,gprof-如何在Linux上分析多线程C ++应用程序?
  2. 【檀越剑指大厂—NIO】NIO学习与使用
  3. vue中,页面使用<keep-alive>缓存,页面切换不调用beforeDestroy和destroyed
  4. Best Reward
  5. 关于查询功能中的重置
  6. 自定义ViewPager实现轮播效果
  7. 《爱情呼叫转移》评论
  8. nginx html页面缓存,页面缓存和nginx缓存
  9. 浮点数运算精度丢失的问题
  10. Linux命令合集,手动精修