reactor原理与实现

一、reactor 模型

什么是reactor?
reactor 是指的多路 I/O 复用中select、poll 、epoll 网络模型下的一种高性能的处理机制。
reactor 释义“反应堆”,是一种事件驱动机制。
普通函数的调用机制:
程序调用某函数,函数执行,程序等待,函数将结果和控制权返回给程序,程序继续处理。
reactor的调用机制:
应用程序不是主动的调用某个 API 完成处理,相反,reactor 逆置了事件处理流程,应用程序需要提供相应的接口并注册到 reactor 上,
如果相应的时间发生,reactor 将主动调用应用程序注册的接口,这些接口又称为“回调函数”。

二、reactor 应用场景

reactor 模式是处理并发 I/O 比较常见的一种模式,用于同步 I/O,中心思想是将所有要处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有 I/O 事件到来或是准备就绪(文件描述符或 socket 可读、写),多路复用器返回并将事先注册的相应 I/O 事件分发到对应的处理器中。

reactor 模型有三个重要的组件:
 多路复用器:由操作系统提供,在 linux 上一般是 select, poll, epoll 等系统调用。
 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
 事件处理器:负责处理特定事件的处理函数。

具体流程如下:

  1. 注册读就绪事件和相应的事件处理器;
  2. 事件分离器等待事件;
  3. 事件到来,激活分离器,分离器调用事件对应的处理器;
  4. 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

三、具体代码实现

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define SERVER_PORT         6668
#define MAX_EPOLL_EVENTS    1024
#define BUFFER_LENGTH		4096

typedef int (*NCALLBACK)(int, int , void *);

typedef struct ntyEvent{
    int fd;
    int events;
    void *arg;

    int (*callback)(int fd, int events, void *arg);
    int status; //status为0,说明该节点不在红黑树上
    int length;
    char buffer[BUFFER_LENGTH];
}ntyEvent_t;

typedef struct ntyReactor{
    int epfd;
    struct ntyEvent *events;
}ntyReactor_t;

int recv_cb_hdl(int fd, int events, void *arg);
int send_cb_hdl(int fd, int events, void *arg);
int accept_cb_hdl(int listen_fd, int events, void *arg);


int socket_init(int port){
    int ret = 0;
    int reuse;
    struct sockaddr_in serv;
    socklen_t address_len = sizeof(struct sockaddr_in);
    
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(listen_fd < 0){
        printf("socket():%s\n", strerror(errno));
        return -1;
    }

    ret = fcntl(listen_fd, F_SETFL, O_NONBLOCK);
    if(ret < 0){
        printf("fcntl() error: %s\n", strerror(errno));
        return -1;
    }

    reuse = 1;
    ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    if(ret < 0){
        printf("setsockopt() error: %s\n", strerror(errno));
        return -1;
    }

    memset(&serv, 0, sizeof(struct sockaddr_in));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(port);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(listen_fd, (struct sockaddr*)&serv, address_len);
    if(ret < 0){
        printf("bind() error: %s\n", strerror(errno));
        return -1;
    }

    ret = listen(listen_fd, 128);
    if(ret < 0){
        printf("listen() error: %s\n", strerror(errno));
        return -1;
    }
    
    return listen_fd;
}

int ntyReactor_init(ntyReactor_t *reactor){
    if(reactor == NULL ) return -1;

    reactor->epfd = epoll_create(1);
    if(reactor->epfd < 0){
        printf("ntyReactor_init() error:%s\n", strerror(errno));
        return -1;
    }

    reactor->events = (ntyEvent_t *)malloc(sizeof(ntyEvent_t) * MAX_EPOLL_EVENTS);
    if(NULL == reactor->events){
        printf("malloc() error!\n");
        close(reactor->epfd);
        return -1;
    }
    memset(reactor->events, 0, sizeof(ntyEvent_t)*MAX_EPOLL_EVENTS);

    return 0;
}

int ntyEventSet(ntyEvent_t *ev, int fd, NCALLBACK callback, void * arg){
    if(ev == NULL) return -1;
    ev->fd = fd;
    ev->events = 0; //默认先设置成0
    ev->arg = arg;
    ev->callback = callback;
    return 0;
}

int ntyEventAdd(int epfd, int events, ntyEvent_t *ev){
    struct epoll_event ep_ev = {0, {0}};
    ep_ev.data.ptr = ev;
    ep_ev.events = ev->events = events;

    int op;
    int ret = 0;
    if(ev->status == 1){
        op = EPOLL_CTL_MOD;
    }else{
		op = EPOLL_CTL_ADD;
		ev->status = 1;
    }

    ret = epoll_ctl(epfd, op, ev->fd, &ep_ev);
    if(ret < 0){
        printf("epoll_ctl():%s\n", strerror(errno));
        return -1;
    }
    
    return 0;
}

int ntyReactorAddListenFd(ntyReactor_t *reactor, int listen_fd, NCALLBACK  acceptor){
    if(reactor == NULL) return -1;
    if(reactor->events == NULL) return -1;
    int ret = 0;

    ret = ntyEventSet(&reactor->events[listen_fd], listen_fd, acceptor, (void *)reactor);
    if(ret < 0){
        printf("ntyEventSet() error!\n");
        return -1;
    }

    ret = ntyEventAdd(reactor->epfd, EPOLLIN, &reactor->events[listen_fd]);
    if(ret < 0){
        printf("nty_event_add() error!\n");
        return -1;
    }
    
    return 0;
}

int ntyEventDel(int epfd, ntyEvent_t *ev){
    struct epoll_event ep_ev = {0, {0}};

    //一个被添加到红黑树上的节点,status的值一定是1,如果status != 1,说明不在树上不用清除掉
    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;
}

//小写转大写功能
void buffer_hdl(char *buffer, int length){
    int i = 0;
    for(i = 0; i < length; i++){
        buffer[i] = toupper(buffer[i]);
    }
}

int send_cb_hdl(int fd, int events, void *arg){
    ntyReactor_t *reactor = (ntyReactor_t *)arg;
    ntyEvent_t *ev = &reactor->events[fd];

    //模拟业务处理
    buffer_hdl(ev->buffer, ev->length);
    
	int len = send(fd, ev->buffer, ev->length, 0);
	if (len > 0) {
		printf("sendbuff = [fd = %d]:%s\n", fd, ev->buffer);
		ntyEventSet(ev, fd, recv_cb_hdl, reactor);
		ntyEventAdd(reactor->epfd, EPOLLIN, ev);
	}else if(len == 0){
        ntyEventDel(reactor->epfd, ev);
        close(ev->fd);
		printf("---send_cb_hdl--client[%d] disconnected!\n", fd);
    } else {
        ntyEventDel(reactor->epfd, ev);
		close(ev->fd);
		printf("--send_cb_hdl--send[fd=%d] error %s\n", fd, strerror(errno));
	}

    return 0;
}


int recv_cb_hdl(int fd, int events, void *arg){
    ntyReactor_t *reactor = (ntyReactor_t *)arg;
    ntyEvent_t *ev = &reactor->events[fd];

    int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);
    if(len > 0){
        ev->length = len;
		ev->buffer[len] = '\0';
    
		printf("recvbuff = [fd = %d]:%s\n", fd, ev->buffer);
        
		ntyEventSet(ev, fd, send_cb_hdl, reactor);
		ntyEventAdd(reactor->epfd, EPOLLOUT, ev);	    
    }else if(len < 0){
        ntyEventDel(reactor->epfd, ev);
		close(ev->fd);
		printf("--recv_cb_hdl--recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
    }else{
        ntyEventDel(reactor->epfd, ev);
		close(ev->fd);
		printf("--recv_cb_hdl--client[%d] disconnected!\n", fd);
    }

    return 0;
}

int accept_cb_hdl(int listen_fd, int events, void *arg){
    ntyReactor_t *reactor =  (ntyReactor_t *)arg;
    if (reactor == NULL) return -1;

    struct sockaddr_in client_addr;
	socklen_t len = sizeof(client_addr);
	int client_fd;
    int i = 0;

    client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);
    if(client_fd < 0)
    {
		if (errno != EAGAIN && errno != EINTR) {
            printf("accept: %s\n", strerror(errno));
            return -1;
		}
	}

    do{
        //标准输入(0)、 标准输出(1)、 标准错误(2)、 listen_fd(3), reactor->epfd(4)占用了前几个文件描述符
        for (i = reactor->epfd + 1; i < MAX_EPOLL_EVENTS; i++) {
			if (reactor->events[i].status == 0) {
				break;
			}
		}

        //用于判定监听的连接是否超过上限值,如果从上面break出来的i等于了最大的监听数,则不能再添加新的连接事件处理,后续优化可做动态扩容
		if (i == MAX_EPOLL_EVENTS) {
			printf("%s: max connect limit[%d]\n", __func__, MAX_EPOLL_EVENTS);
			break;
		}

        int flag = 0;
		if ((flag = fcntl(client_fd, F_SETFL, O_NONBLOCK)) < 0) {
			printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
			break;
		}
        
        ntyEventSet(&reactor->events[client_fd], client_fd, recv_cb_hdl, reactor);
        ntyEventAdd(reactor->epfd, EPOLLIN, &reactor->events[client_fd]);
    }while(0);

    printf("new connect [%s:%d], clientfd[%d], cur_unused_index = [%d]\n",\
            inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd, i);

    return 0;
}

int ntyReactorLoopRun(ntyReactor_t *reactor){
    if(reactor == NULL) return -1;
    if(reactor->epfd < 0) return -1;
    if(reactor->events == NULL) return -1;

    struct epoll_event events[MAX_EPOLL_EVENTS];
    int nready = 0;
    int i = 0;
    
    while(1){
        nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, -1);
        if(nready < 0){
            if(errno == EINTR){
                //被信号打断,重新loop
                continue;
            }
            printf("epoll_wait error(%s), exit---\n",strerror(errno));
            return -1;
        }

        for(i = 0; i < nready; i++){
            ntyEvent_t *ev = (ntyEvent_t*)events[i].data.ptr;
            if((ev->events & EPOLLIN) && events[i].events & EPOLLIN){
                ev->callback(ev->fd, events[i].events, ev->arg);
            }

            if((ev->events & EPOLLOUT) && events[i].events & EPOLLOUT){
                ev->callback(ev->fd, events[i].events, ev->arg);
            }
        }
    }

    return 0;
}

int ntyReactorDestory(ntyReactor_t        *reactor){
    if(NULL == reactor) return -1;
    
    close(reactor->epfd);
    if(reactor->events != NULL)
	    free(reactor->events);
    free(reactor);    
    return 0;
}

int main(int argc, char *argv[]){
    int ret = 0;
    int serv_port = SERVER_PORT;
    
    if(argc == 2){
        serv_port = atoi(argv[1]);
    }

    //1、初始化socket()
    int listen_fd = socket_init(serv_port);
    if(listen_fd < 0){
        printf("init_socket() error!\n");
        return -1;
    }

    printf("socket_init:listen_fd = [%d]\n", listen_fd);
    //2、初始 reactor
    ntyReactor_t *reactor = (ntyReactor_t *)malloc(sizeof(ntyReactor_t));
    if(NULL == reactor){
        printf("malloc() error!\n");
    }
    ret = ntyReactor_init(reactor);
    if(ret < 0){
        printf("ntyReactor_init() error!\n");
    }
    printf("ntyReactor_init:reactor->epfd = [%d]\n",reactor->epfd);

    //3、listen_fd 事件处理回调函数注册到reactor监听
	ret = ntyReactorAddListenFd(reactor, listen_fd, accept_cb_hdl);
    if(ret < 0){
        printf("ntyreactor_addlistener() error\n");
        return -1;
    }
    
    //4、recator模型下的epoll_wait()函数等待事件发生
    ret = ntyReactorLoopRun(reactor);
    if(ret < 0){
        printf("ntyreactor_run() error!\n");
    }
    
    //5、reactor 销毁
    ret = ntyReactorDestory(reactor);
    if(ret < 0){
        printf("ntyReactorDestory() error!\n");
    }
	close(listen_fd);
    return 0;
}
编译执行及其调试:
[root@localhost socket-reactor-mode]# ./test 8888
socket_init:listen_fd = [3]
ntyReactor_init:reactor->epfd = [4]
new connect [127.0.0.1:36798], clientfd[5], cur_unused_index = [5]
recvbuff = [fd = 5]:hello linux!
sendbuff = [fd = 5]:HELLO LINUX!
new connect [127.0.0.1:36800], clientfd[6], cur_unused_index = [6]
recvbuff = [fd = 6]:hello China!
sendbuff = [fd = 6]:HELLO CHINA!

new connect [127.0.0.1:36802], clientfd[7], cur_unused_index = [7]
recvbuff = [fd = 7]:abcdefghigkmon
sendbuff = [fd = 7]:ABCDEFGHIGKMON

new connect [172.16.10.223:58039], clientfd[8], cur_unused_index = [8]
recvbuff = [fd = 8]:http://www.cmsoft.cn QQ:10865600
sendbuff = [fd = 8]:HTTP://WWW.CMSOFT.CN QQ:10865600
--recv_cb_hdl--client[6] disconnected!
new connect [127.0.0.1:36804], clientfd[6], cur_unused_index = [6]
recvbuff = [fd = 6]:hello china!
sendbuff = [fd = 6]:HELLO CHINA!

四、reactor模式的优点

reactor 模式是编写高性能网络服务器的必备技术之一, 有如下优点:
 响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进
程的切换开销;
 可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;
 可复用性,reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性;

五、总结

reactor 模型开发效率上比起直接使用 IO 复用要高,它通常是单线程的,设计目标是希望单线程使用一颗 CPU 的全部资源,但也有附带优点,即每个事件处理中很多时候可以不考虑共享资源的互斥访问。可是缺点也是明显的,现在的硬件发展,已经不再遵循摩尔定律,CPU 的频率受制于材料的限制不再有大的提升,而改为是从核数的增加上提升能力,当程序需要使用多核资源时,reactor 模型就会悲剧, 为什么呢?如果程序业务很简单,例如只是简单的访问一些提供了并发访问的服务,就可以直接开启多个反应堆,每个反应堆对应一颗 CPU 核心,这些反应堆上跑的请求互不相关,这是完全可以利用多核的。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Reactor模式是一种事件驱动的设计模式,它的核心思想是将事件处理程序与I/O操作分离开来,通过事件分发器来处理所有的I/O事件,从而提高系统的可扩展性和可维护性。 Reactor模式的实现原理可以分为以下几个步骤: 1. 事件分发器:Reactor模式中的事件分发器负责监听所有的I/O事件,包括读写事件、连接事件和关闭事件等。一旦有事件发生,事件分发器会将事件分发给相应的事件处理程序进行处理。 2. 事件处理程序:事件处理程序是Reactor模式中的核心组件,它负责处理所有的I/O事件。每个事件处理程序都需要实现一个统一的接口,包括处理读写事件、连接事件和关闭事件等。 3. 事件处理器:事件处理器是一种通用的I/O操作处理器,它负责将I/O操作转化为事件,并将事件注册到事件分发器中。在Reactor模式中,所有的I/O操作都需要通过事件处理器进行注册和管理。 4. 多路复用器:多路复用器是一种高效的I/O事件处理机制,它可以同时处理多个I/O事件,从而提高系统的性能和可伸缩性。在Reactor模式中,多路复用器通常被用来监听所有的I/O事件,并将事件分发给相应的事件处理程序进行处理。 总的来说,Reactor模式的实现原理是基于事件驱动的设计思想,通过事件分发器、事件处理程序、事件处理器和多路复用器等组件来实现高效的I/O事件处理机制,从而提高系统的可扩展性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值