Linux -- Reactor
结构
1. handles
资源的标志.这些资源通常包含网络连接,文件,定时器,同步对象等.handles 被用在注册服务器来标记socket,以便同步事件复用(Synchronous Event Demultiplexer)能等待这些资源就绪.注册服务器对两种事件感兴趣,一是连接事件(connetion events)一是可读事件(read events),分别代表传入新的客户端连接和记录数据.注册服务器对每个客户端都保持一个独立的连接.每一条连接对应server上的一个socket ,这个socket在reactor模式中称为handle.在这个模式中,具体的来说,handle就是concrete event handler的文件描述符
2. synchronous events demultiplexer
阻塞等待handles集之中有事件就绪.当有handle就绪时返回.其中一个常用的I/O事件复用就是select()函数.
3. initiation dispatcher
一个注册,删除,分配event handlers的接口. synchronous events demultiplexer负责等待新事件发生.当synchronous events demultiplexer检测到新事件,它将通知initiation dispatcher 去调用特定的事件处理器(event handlers).事件包括:连接接受事件(connection acceptance events),数据读写事件(data i/o事件),超时事件(time out events)
4. event handler
抽象类(或接口).包含一个hook method等待具体子类实现
5. concrete event handler
实现了hook method,也就是处理特定事件的特定方法.应用程序在initiation dispatcher中注册concrete event handler去处理特定类型的事件.当事件到达,inittiation dispatcher调用该类事件对应的concrete event handler的hook method.
协作
1 : 注册event handler
应用程序在initiation dispatcher中注册concrete event handler,这就意味着应用程序把某类特定事件(handle)和此concrete event handler关联在了一起.之后若有此类事件发生,initiation dispatcher 将通知(notify)此concrete event handler.
Initiation dispatcher 要求每个event handler回传他们自己的handle,其实就是他们自己的文件描述符fd.以便initiation dispatcher 记录.
2 : 启动event loop
当所有的event handlers注册完毕,应用程序调用initiation dispatcher的handle_events()函数启动event loop.event loop的具体代码见下.
Handle_events(){
For(;;){ //event loop
Select(handlers); //select 无事件时阻塞
Foreach h in handlers //遍历handlers,处理就绪的事件
h.handle_event(event)
endforeach
}
}
这时,initiation dispatcher使用Synchronous Event Demultiplexer等待这些handles上的事件发生.
3 : 事件就绪
当一个对应着事件源的handle就绪, Synchronous Event Demultiplexer通知initiation dispatcher.例如:一个TCP socket 可读就绪
4 : 处理事件
收到某handle就绪的通知 后,Initiation dispatcher 触发对应event handler的hook method来处理就绪事件
Linux中的epoll
Epoll简直是为reactor而生的.epoll_create()创建的就是initiation dispatcher,又名reactor.
Epoll_ctl()可以实现了注册,删除,修改handlers(或称 event).epoll_wait()就是synchronous events demultiplexer.
标准编程:
#define IPADDRESS "127.0.0.1"
#define PORT 8787
#define MAXSIZE 1024
#define LISTENQ 5
#define FDSIZE 1000
#define EPOLLEVENTS 100 listenfd = socket_bind(IPADDRESS,PORT); struct epoll_event events[EPOLLEVENTS]; //创建一个描述符
epollfd = epoll_create(FDSIZE); //添加监听描述符事件
add_event(epollfd,listenfd,EPOLLIN); //循环等待 event loop
for ( ; ; ){
//该函数返回已经准备好的描述符事件数目
ret = epoll_wait(epollfd,events,EPOLLEVENTS,-);
//处理接收到的连接
handle_events(epollfd,events,ret,listenfd,buf);
} //事件处理函数
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
{
int i;
int fd;
//进行遍历;这里只要遍历已经准备好的io事件。num并不是当初epoll_create时的FDSIZE。
for (i = ;i < num;i++)
{
fd = events[i].data.fd;
//根据描述符的类型和事件类型进行处理
if ((fd == listenfd) &&(events[i].events & EPOLLIN))
handle_accpet(epollfd,listenfd);
else if (events[i].events & EPOLLIN)
do_read(epollfd,fd,buf);
else if (events[i].events & EPOLLOUT)
do_write(epollfd,fd,buf);
}
} //添加事件
static void add_event(int epollfd,int fd,int state){
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
} //处理接收到的连接
static void handle_accpet(int epollfd,int listenfd){
int clifd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
if (clifd == -)
perror("accpet error:");
else {
printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //添加一个客户描述符和事件
add_event(epollfd,clifd,EPOLLIN);
}
} //读处理
static void do_read(int epollfd,int fd,char *buf){
int nread;
nread = read(fd,buf,MAXSIZE);
if (nread == -) {
perror("read error:");
close(fd); //记住close fd
delete_event(epollfd,fd,EPOLLIN); //删除监听
}
else if (nread == ) {
fprintf(stderr,"client close.\n");
close(fd); //记住close fd
delete_event(epollfd,fd,EPOLLIN); //删除监听
}
else {
printf("read message is : %s",buf);
//修改描述符对应的事件,由读改为写
modify_event(epollfd,fd,EPOLLOUT);
}
} //写处理
static void do_write(int epollfd,int fd,char *buf) {
int nwrite;
nwrite = write(fd,buf,strlen(buf));
if (nwrite == -){
perror("write error:");
close(fd); //记住close fd
delete_event(epollfd,fd,EPOLLOUT); //删除监听
}else{
modify_event(epollfd,fd,EPOLLIN);
}
memset(buf,,MAXSIZE);
} //删除事件
static void delete_event(int epollfd,int fd,int state) {
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
} //修改事件
static void modify_event(int epollfd,int fd,int state){
struct epoll_event ev;
ev.events = state;
ev.data.fd = fd;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
}
Linux -- Reactor的更多相关文章
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- 框架篇:见识一下linux高性能网络IO+Reactor模型
前言 网络I/O,可以理解为网络上的数据流.通常我们会基于socket与远端建立一条TCP或者UDP通道,然后进行读写.单个socket时,使用一个线程即可高效处理:然而如果是10K个socket连接 ...
- 【Linux 网络编程】生动讲解 Reactor 模式与 Proactor 模式
五种 I/O 模型 先花费点时间了解这几种 I/O 模型,有助于后面的理解. 阻塞 I/O 与非阻塞 I/O 阻塞和非阻塞的概念能应用于所有的文件描述符,而不仅仅是 socket.我们称阻塞的文件描述 ...
- Linux -- Proactor(及其与Reactor的比较)
高并发服务器常由多线程+IO复用服务器(one event loop per thread) 两种I/O多路复用模式:Reactor和Proactor 一般地,I/O多路复用机制都依赖于一个事件多路分 ...
- Linux C++ Reactor模式
文件结构 reactor_main.cpp reactor_server.cpp reactor_server.h CMakeLists.txt CMakeLists.txt cmake_minimu ...
- Linux C 收藏
某招聘要求:熟悉高性能分布式网络服务端设计开发,熟悉epoll.多线程.异步IO.事件驱动等服务端技术: <UNIX环境高级编程(第3版)>apue.h等源码文件的编译安装 <UNI ...
- 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式
为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...
- linux c 笔记-4 工程项目阅读推荐
作者:周子涵链接:https://www.zhihu.com/question/27705862/answer/37738315来源:知乎著作权归作者所有,转载请联系作者获得授权. 转自网上不知道什么 ...
- Python Twisted、Reactor
catalogue . Twisted理论基础 . 异步编程模式与Reactor . Twisted网络编程 . reactor进程管理编程 . Twisted并发连接 1. Twisted理论基础 ...
随机推荐
- Maven创建本地仓库
1:创建仓库目录 在D盘Program Files目录下创建repository目录 2:修改settings.xml D:\ProgramFiles\repository 是我们创建的本地 ...
- virtual box启动error
启动 VirtualBox 的时候提示 “获取 VirtualBox COM 对象失败” 错误详情 “获取 VirtualBox COM 对象失败” 的错误详情: 获取 VirtualBox CO ...
- redis-4.0.14 cluster 配置实战
1.操作系统配置 切换到root用户修改配置sysctl.conf vim /etc/sysctl.conf # 添加配置: vm.max_map_count= vm.overcommit_memor ...
- 学习路线 (转自 https://mp.weixin.qq.com/s/_FIGSda6wWL-5LXMQAk3IA )
- JavaScript 廖
=============== JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>中 ======注释 // 以双斜杠开头直到行末 ...
- mongodb 4.0.5 集群搭建五台集群
配置文件采用yaml方式来配置 生产中取消了仲裁者的角色,因为仲裁者也不会存储数据,只是起到选举的作用,线上为了保证数据安全,每份数据都会配置两个副本集,也就是每份数据存储了三份. 优化配置,采用五台 ...
- [Bob]Collectors Problem
https://vjudge.net/problem/UVA-10779#author=0 网络流 1.Bob向他有的贴纸连边,流量为他有的贴纸数量 2.每一种贴纸向汇点连流量为1的边 3.其余人,如 ...
- Tree-shaking
Tree-shaking 字面意思就是 摇晃树, 其实就是去除那些引用的但却没有使用的代码. Tree-shaking 概念最早由 Rollup.js 提出,后来在 webpack2 中被引入进来,但 ...
- AWS API Gateway Swagger定义
导出Swagger接口定义文件 在AWS API Gateway界面上,可以导出swagger接口定义文件. 而后利用Node js swagger-ui 依赖,生成swagger接口地址 Cloud ...
- NNDL练习——Numpy的简单使用
总结自nndl_exercise Numpy导入 import numpy as np 数组/矩阵的创建 a=np.array([1,2,3]) b=np.array([[1,2],[3,4]]) c ...