背景
以前的处理IO的方法都是一个请求一个线程,效率不高
简介
reactor可理解为就是对大量IO进行集中式管理(如用epoll),每个IO走不同的回调函数。
Reactor和Proactor是高效的事件处理模型
reactor是对epoll的一层封装 ,epoll是对io进行管理,reactor将对io的管理转化为对事件的管理
Reactor释义**“反应堆”,是一种事件驱动**机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为"回调函数”。
Reactor模式是处理并发IO比较常见的一种模式,用于同步IO,中心思想是将所有要处理的IO事件注册到一个中心IO多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有IO事件到来或是准备就绪(文件描述符或socket可读、写),多路复用器返回并将事先注册的相应IO事件分发到对应的处理器中。
Reactor模型有三个重要的组件:
-
多路复用器:由操作系统提供,在linux上一般是select, poll, epoll等系统调用。
-
事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
-
事件处理器:负责处理特定事件的处理函数(回调函数)。
对socket和事件的封装
fd的组织方式是:用nitem
封装fd和其对应的读写缓冲区、事件等属性;用定长数组存多个nitem
, 再用一个itemblock
封装这个数组,从而实现大量存储item,即采用 链表+数组的组织方式。
相关结构体的具体实现代码如下:
typedef int NCALLBACK(int fd, int event, void *arg);
// 包装fd以及其读写缓冲区和回调
struct nitem {
int fd;
int status;
int events;
void *arg;
// 不同的IO对应不同的回调
NCALLBACK *readcb; // epollin
NCALLBACK *writecb; // epollout
NCALLBACK *acceptcb; // epollin
// 读写缓冲区
unsigned char sbuffer[BUFFER_LENGTH];
int slength;
unsigned char rbuffer[BUFFER_LENGTH];
int rlength;
};
// !!一个block存多个item,block用链表组织,nitem 也是用链表组织
struct itemblock{
struct itemblock *next;
struct nitem *items; // nitem数组
};
struct reactor {
int epfd;
struct itemblock *head;
};
服务端接收到客户端发来消息并回发的流程
C语言完整代码
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAXLINE 4096
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENT 1024
#define NOSET_CB 0
#define READ_CB 1
#define WRITE_CB 2
#define ACCEPT_CB 3
typedef int NCALLBACK(int fd, int event, void *arg);
// 包装fd以及其读写缓冲区和回调
struct nitem
{
int fd;
int status;
int events;
void *arg;
// 不同的IO对应不同的回调
NCALLBACK *readcb; // epollin
NCALLBACK *writecb; // epollout
NCALLBACK *acceptcb; // epollin
// 读写缓冲区
unsigned char sbuffer[BUFFER_LENGTH];
int slength;
unsigned char rbuffer[BUFFER_LENGTH];
int rlength;
};
// !!一个block存多个item,block用链表组织,nitem 也是用链表组织
struct itemblock
{
struct itemblock *next;
struct nitem *items; // nitem数组
};
struct reactor
{
int epfd;
struct itemblock *head;
};
int init_reactor(struct reactor *r);
int read_callback(int fd, int event, void *arg);
int write_callback(int fd, int event, void *arg);
int accept_callback(int fd, int event, void *arg);
struct reactor *instance = NULL;
// singleton,全局只提供一个reactor访问点
struct reactor *getInstance(void)
{
if(instance == NULL)
{
instance = malloc(sizeof(struct reactor));
if(instance == NULL) return NULL;
memset(instance, 0x00, sizeof(struct reactor));
if(0 > init_reactor(instance))
{
free(instance);
return NULL;
}
}
return instance;
}
int nreactor_set_event(int fd, NCALLBACK cb, int event, void *arg)
{
struct reactor *r = getInstance();
struct epoll_event ev = {0};
if(event == READ_CB)
{
r->head->items[fd].fd = fd;
r->head->items[fd].readcb = cb;
r->head->items[fd].arg = arg;
ev.events = EPOLLIN;
}
else if(event == WRITE_CB)
{
r->head->items[fd].fd = fd;
r->head->items[fd].writecb = cb;
r->head->items[fd].arg = arg;
ev.events = EPOLLOUT;
}
else if(event == ACCEPT_CB)
{
r->head->items[fd].fd = fd;
r->head->items[fd].acceptcb = cb;
r->head->items[fd].arg = arg;
ev.events = EPOLLIN;
}
// ptr指向fd对应的nitem
ev.data.ptr = &r->head->items[fd];
// socket原来的兴趣事件类型为NOTSET_CB,即初始状态
if(r->head->items[fd].events == NOSET_CB)
{
if(epoll_ctl(r->epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
{
printf("epoll_ctl EPOLL_CTL_ADD failed: %d\n", errno);
return -1;
}
r->head->items[fd].events = event;
}
else if(r->head->items[fd].events != event)
{
if(epoll_ctl(r->epfd, EPOLL_CTL_MOD, fd, &ev) < 0)
{
printf("epoll_ctl EPOLL_CTL_MOD failed: %d\n", errno);
return -1;
}
r->head->items[fd].events = event;
}
return 0;
}
int nreactor_del_event(int fd, NCALLBACK cb, int event, void *arg)
{
struct reactor *r = getInstance();
struct epoll_event ev = {0};
ev.data.ptr = arg;
epoll_ctl(r->epfd, EPOLL_CTL_DEL, fd, &ev);
r->head->items[fd].events = 0; //
return 0;
}
int write_callback(int fd, int event, void *arg)
{
struct reactor *R = getInstance();
unsigned char *sbuffer = R->head->items[fd].sbuffer;
int length = R->head->items[fd].slength;
int ret = send(fd,sbuffer, length, 0);
if(ret < length) // 未写完
{
nreactor_set_event(fd, write_callback, WRITE_CB, NULL);
}
else
{
nreactor_set_event(fd, read_callback, READ_CB, NULL);
}
return 0;
}
int read_callback(int fd, int event, void *arg)
{
struct reactor *R = getInstance();
unsigned char *buffer = R->head->items[fd].rbuffer;
// LT
int ret = recv(fd, buffer, BUFFER_LENGTH, 0);
if(ret == 0) { // connection closed
nreactor_del_event(fd, NULL, 0, NULL);
close(fd);
}
else if(ret > 0)
{
// echo
unsigned char *sbuffer = R->head->items[fd].sbuffer;
memcpy(sbuffer, buffer, ret);
R->head->items[fd].slength = ret;
printf("readcb: %s\n", sbuffer);
// 对fd注册可写事件,事件到来则执行 write_callback, eventloop中传参
nreactor_set_event(fd, write_callback, WRITE_CB, NULL);
}
}
int accept_callback(int fd, int event, void *arg)
{
int connfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
if((connfd = accept(fd, (struct sockaddr *)&client, &len)) == -1)
{
printf("accept socket error : %s(errno): %d \n", strerror(errno), errno);
return 0;
}
// 在epoll内核事件表添加connfd的读事件监控
nreactor_set_event(connfd, read_callback, READ_CB, NULL);
}
// 初始化服务器,在某个IP和PORT进行监听
int init_server(int port)
{
int listenfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
// 设置端口复用
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt, sizeof(opt));
memset(&servaddr, 0x00, sizeof(servaddr));
servaddr.sin_family = AF_INET;
// 主机序-》网络序(大端),大端:数据低位存在内存高位
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
if(listen(listenfd, 10) == -1)
{
printf("listens socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
return listenfd;
}
int init_reactor(struct reactor *r)
{
if(r == NULL) return -1;
int epfd = epoll_create(1);
r->epfd = epfd;
// fd->item
// 头节点
r->head = (struct itemblock*)malloc(sizeof(struct itemblock));
if(r->head == NULL)
{
close(epfd);
return -2;
}
memset(r->head, 0, sizeof(struct itemblock));
// ???问题:似乎没有item数组满的时候,增加itemblock节点及其指向的nitem数组的操作
// 初始化第一个block节点的item数组
r->head->items = malloc(MAX_EPOLL_EVENT * sizeof(struct nitem));
if(r->head->items == NULL)
{
free(r->head);
close(epfd);
return -2;
}
memset(r->head->items, 0, MAX_EPOLL_EVENT * sizeof(struct nitem));
r->head->next = NULL;
return 0;
}
// eventloop
int reactor_loop(int listenfd)
{
struct reactor *R = getInstance();
struct epoll_event events[MAX_EPOLL_EVENT] = {0};
while(1)
{
int nready = epoll_wait(R->epfd, events, MAX_EPOLL_EVENT, 5);
if(nready == -1)
{
continue;
}
int i = 0;
for(int i = 0; i < nready; i++)
{
// nreactor_set_event给设置的nitem指针
struct nitem *item = events[i].data.ptr;
int connfd = item->fd;
if(connfd == listenfd) // 监听fd
{
// nreactor_set_event中设置的回调函数
item->acceptcb(listenfd, 0, NULL);
}
else // 连接fd
{
// 事件被设置为可读时,回调也被一同设置了
if(events[i].events & EPOLLIN)
{
item->readcb(connfd, 0, NULL);
}
// 事件被设置为可写时,回调也被一同设置了
if(events[i].events & EPOLLOUT)
{
item->writecb(connfd, 0, NULL);
}
}
}
}
}
int main(int argc, char ** argv)
{
int listenfd = init_server(8888);
nreactor_set_event(listenfd, accept_callback, ACCEPT_CB, NULL);
reactor_loop(listenfd);
return 0;
}
测试
编译
gcc -o reactor_mulcb reactor_mulcb.c
测试
成功实现回发数据