介绍
Linux环境使用epoll实现基本的Reactor单线程模型,主要用来记录代码思路并实现基本功能,不做复杂逻辑。
Reactor头文件
#ifndef REACTOR_H
#define REACTOR_H
#include <stdio.h>
#include <unistd.h> // read write
#include <fcntl.h> // fcntl
#include <sys/types.h> // listen
#include <sys/socket.h> // socket
#include <errno.h> // errno
#include <arpa/inet.h> // inet_addr htons
#include <sys/epoll.h>
#include <stdlib.h> // malloc
#include <string.h> // memcpy memmove
#define MAX_EVENT_NUM 512
#define MAX_CONN ((1<<16)-1)
#define MSG_SIZE 1024
//事件回调
typedef void (*event_cb)(int fd, int eventType, void *privdata);
typedef void (*error_cb)(int fd, char *err);
struct Reactor
{
int epfd;
int listenfd;
int stop;
struct epoll_event events[MAX_EVENT_NUM];
};
struct CustomEvent
{
int fd;
Reactor *reactor;
char recv[MSG_SIZE];
char send[MSG_SIZE];
event_cb readCb;
event_cb writeCb;
error_cb errorCb;
};
//callback func
void read_cb(int fd, int eventType, void *privdata);
void accept_cb(int fd, int eventType, void *privdata);
//func
int set_nonblock(int fd);
Reactor *create_reactor();
void release_reactor(Reactor *r);
void eventloop_once(Reactor *r, int timeout);
void start_eventloop(Reactor *r);
void stop_eventloop(Reactor *r);
CustomEvent *new_event(Reactor *r, int fd, event_cb readcb, event_cb writecb, error_cb errcb);
void free_event(CustomEvent *e);
int add_event(Reactor *r, int eventType, CustomEvent *e);
int del_event(Reactor *r, CustomEvent *e);
int create_server(Reactor *r, short port, event_cb accept);
#endif // REACTOR_H
Reactor实现文件
#include "reactor.h"
void read_cb(int fd, int eventType, void *privdata)
{
CustomEvent *e = (CustomEvent *)privdata;
int n = read(fd, e->recv, MSG_SIZE);
if (n == 0)
{
printf("close connection fd = %d\n", fd);
del_event(e->reactor, e);
close(fd);
return;
}
else if (n < 0)
{
printf("read error fd = %d err = %s\n", fd, strerror(errno));
if (e->errorCb)
e->errorCb(fd, strerror(errno));
del_event(e->reactor, e);
close(fd);
return;
}
else
{
printf("recv data from client:%s\n", e->recv);
memset(e->recv, 0, MSG_SIZE);
//回写
char msg[] = "replay info";
write(e->fd, msg, sizeof(msg));
}
}
void accept_cb(int fd, int eventType, void *privdata)
{
CustomEvent *e = (CustomEvent *)privdata;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
socklen_t len = sizeof(addr);
int clientfd = accept(fd, (struct sockaddr*)&addr, &len);
if (clientfd <= 0)
{
printf("accept failed\n");
return;
}
char str[INET_ADDRSTRLEN] = {0};
printf("recv from %s at port %d\n", inet_ntop(AF_INET, &addr.sin_addr, str, sizeof(str)), ntohs(addr.sin_port));
CustomEvent *ce = new_event(e->reactor, clientfd, read_cb, 0, 0);
add_event(ce->reactor, EPOLLIN, ce);
set_nonblock(clientfd);
}
int set_nonblock(int fd)
{
int flag = fcntl(fd, F_GETFL, 0);
return fcntl(fd, F_SETFL, flag | O_NONBLOCK);
}
Reactor *create_reactor()
{
Reactor *r = (Reactor *)malloc(sizeof(Reactor));
r->epfd = epoll_create(1);
r->listenfd = 0;
r->stop = 0;
memset(r->events, 0, sizeof(struct epoll_event) * MAX_EVENT_NUM);
return r;
}
void release_reactor(Reactor *r)
{
stop_eventloop(r);
close(r->epfd);
free(r);
}
void eventloop_once(Reactor *r, int timeout)
{
int n = epoll_wait(r->epfd, r->events, MAX_EVENT_NUM, timeout);
for (int i = 0; i < n; i++)
{
struct epoll_event *e = &r->events[i];
int mask = e->events;
if (e->events & EPOLLERR)
mask |= EPOLLIN | EPOLLOUT;
if (e->events & EPOLLHUP)
mask |= EPOLLIN | EPOLLOUT;
CustomEvent *ce = (CustomEvent*)e->data.ptr;
if (mask & EPOLLIN)
{
if (ce->readCb)
ce->readCb(ce->fd, EPOLLIN, ce);
}
if (mask & EPOLLOUT)
{
if (ce->writeCb)
{
ce->writeCb(ce->fd, EPOLLOUT, ce);
}
else
{
//write buf
}
}
}
}
void start_eventloop(Reactor *r)
{
while (!r->stop)
{
eventloop_once(r, -1);
}
}
void stop_eventloop(Reactor *r)
{
r->stop = 1;
}
CustomEvent *new_event(Reactor *r, int fd, event_cb readcb, event_cb writecb, error_cb errcb)
{
CustomEvent *e = (CustomEvent *)malloc(sizeof(CustomEvent));
e->reactor = r;
e->fd = fd;
memset(e->recv, 0, MSG_SIZE);
memset(e->send, 0, MSG_SIZE);
e->readCb = readcb;
e->writeCb = writecb;
e->errorCb = errcb;
return e;
}
void free_event(CustomEvent *e)
{
free(e);
}
int del_event(Reactor *r, CustomEvent *e)
{
epoll_ctl(r->epfd, EPOLL_CTL_DEL, e->fd, NULL);
free_event(e);
return 0;
}
int add_event(Reactor *r, int eventType, CustomEvent *e)
{
struct epoll_event ev;
ev.events = eventType;
ev.data.ptr = e;
if (epoll_ctl(r->epfd, EPOLL_CTL_ADD, e->fd, &ev) == -1)
{
printf("add event err fd = %d\n", e->fd);
return 1;
}
return 0;
}
int create_server(Reactor *r, short port, event_cb accept)
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
printf("create listenfd error!\n");
return -1;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int reuse = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int)) == -1)
{
printf("reuse address error: %s\n", strerror(errno));
return -1;
}
if (bind(listenfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0)
{
printf("bind error %s\n", strerror(errno));
return -1;
}
if (listen(listenfd, 5) < 0)
{
printf("listen error %s\n", strerror(errno));
return -1;
}
if (set_nonblock(listenfd) < 0)
{
printf("set_nonblock error %s\n", strerror(errno));
return -1;
}
r->listenfd = listenfd;
CustomEvent *e = new_event(r, listenfd, accept, 0, 0);
add_event(r, EPOLLIN, e);
printf("listen port : %d\n", port);
return 0;
}
调用
#include <iostream>
#include "reactor.h"
using namespace std;
int main()
{
Reactor *reactor = create_reactor();
if (create_server(reactor, 8989, accept_cb) < 0)
{
release_reactor(reactor);
return 1;
}
start_eventloop(reactor);
release_reactor(reactor);
return 0;
}