Last active
February 26, 2019 13:24
-
-
Save baixiangcpp/be7872baa82d9b9c400b17a2b0fe5fe3 to your computer and use it in GitHub Desktop.
A simple libevent-like reactor demo base on epoll api
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* @Author: baixiangcpp@gmail.com | |
* @Date: 2018-07-28 11:04:52 | |
* @Last Modified by: baixiangcpp@gmail.com | |
* @Last Modified time: 2018-07-28 11:07:04 | |
* @usage: An implementation of reactor design patterns base on epoll. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/epoll.h> | |
#include <fcntl.h> | |
#include <strings.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#define MAX_EVENTS 100 | |
#define BUF_SIZE 1024 | |
void onError(const char *reason) | |
{ | |
perror(reason); | |
exit(EXIT_FAILURE); | |
} | |
typedef void (*callback)(int fd, int events, void *arg); | |
struct event | |
{ | |
int fd; | |
int events; | |
void *arg; | |
callback cb; | |
char *buf; | |
int bufsize; | |
struct event_base *base; | |
}; | |
struct event_base | |
{ | |
int epollfd; | |
}; | |
struct event *event_new(struct event_base *base, int fd, int events, callback cb, void *arg) | |
{ | |
struct event *ev = malloc(sizeof(struct event)); | |
if (!ev) | |
return NULL; | |
ev->fd = fd; | |
ev->events = events; | |
ev->cb = cb; | |
ev->base = base; | |
ev->arg = arg; | |
ev->buf = malloc(BUF_SIZE); | |
ev->bufsize = BUF_SIZE; | |
bzero(ev->buf, BUF_SIZE); | |
return ev; | |
} | |
void event_add(struct event *ev) | |
{ | |
struct epoll_event epollev; | |
epollev.events = ev->events; | |
epollev.data.ptr = ev; | |
if (epoll_ctl(ev->base->epollfd, EPOLL_CTL_ADD, ev->fd, &epollev) == -1) | |
onError("epoll_ctl add fd"); | |
} | |
void event_del(struct event *ev) | |
{ | |
if (epoll_ctl(ev->base->epollfd, EPOLL_CTL_DEL, ev->fd, NULL) == -1) | |
onError("epoll_ctl add fd"); | |
close(ev->fd); | |
free(ev->buf); | |
free(ev); | |
} | |
void event_mod(struct event *ev, int events) | |
{ | |
struct epoll_event epollev; | |
ev->events = events; | |
epollev.events = events; | |
epollev.data.ptr = ev; | |
if (epoll_ctl(ev->base->epollfd, EPOLL_CTL_MOD, ev->fd, &epollev) == -1) | |
onError("epoll_ctl add fd"); | |
} | |
void recvdata(struct event *ev) | |
{ | |
int ret = read(ev->fd, ev->buf, ev->bufsize); | |
if (ret <= 0) // error or peer close | |
{ | |
event_del(ev); | |
return; | |
} | |
ev->buf[ret] = 0; | |
event_mod(ev, EPOLLOUT); | |
} | |
void senddata(struct event *ev) | |
{ | |
int ret = write(ev->fd, ev->buf, strlen(ev->buf)); | |
if (ret <= 0) // error or peer close | |
{ | |
event_del(ev); | |
return; | |
} | |
event_mod(ev, EPOLLIN); | |
} | |
void handle_event(int fd, int events, void *arg) | |
{ | |
struct event *ev = (struct event *)arg; | |
int ret = 0; | |
if (events == EPOLLIN) | |
recvdata(ev); | |
else if (events == EPOLLOUT) | |
senddata(ev); | |
} | |
void handle_conn(int fd, int events, void *arg) | |
{ | |
struct event *ev = (struct event *)arg; | |
struct event_base *base = ev->base; | |
struct sockaddr_in addr = {0}; | |
socklen_t addrlen = sizeof(addr); //value-result argument | |
char addrstr[20] = {0}; | |
int socket = accept(fd, (struct sockaddr *)&addr, &addrlen); | |
if (socket == -1) | |
onError("accpet"); | |
inet_ntop(AF_INET, &addr.sin_addr, addrstr, INET_ADDRSTRLEN); | |
printf("Accpet a new client,client addr is %s .\n", addrstr); | |
struct event *newev = event_new(base, socket, EPOLLIN, handle_event, NULL); | |
if (!newev) | |
onError("event_new"); | |
event_add(newev); | |
} | |
int initServSocket(struct event_base *base, int port) | |
{ | |
struct sockaddr_in sin; | |
int servsock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); | |
if (servsock == -1) | |
onError("socket()"); | |
bzero(&sin, sizeof(sin)); | |
sin.sin_family = AF_INET; | |
sin.sin_addr.s_addr = INADDR_ANY; | |
sin.sin_port = htons(port); | |
if (bind(servsock, (struct sockaddr *)&sin, sizeof(sin)) == -1) | |
onError("bind()"); | |
listen(servsock, SOMAXCONN); | |
return servsock; | |
} | |
struct event_base *base_new() | |
{ | |
struct event_base *base = malloc(sizeof(struct event_base)); | |
if (!base) | |
onError("base_new"); | |
int epollfd = epoll_create1(0); | |
if (epollfd == -1) | |
onError("epoll_create1"); | |
base->epollfd = epollfd; | |
return base; | |
} | |
void base_dispatch(struct event_base *base) | |
{ | |
int nfds; | |
struct epoll_event events[MAX_EVENTS]; | |
for (;;) | |
{ | |
int n = 0; | |
nfds = epoll_wait(base->epollfd, events, MAX_EVENTS, -1); | |
if (nfds == -1) | |
{ | |
perror("epoll_wait"); | |
exit(EXIT_FAILURE); | |
} | |
for (n = 0; n < nfds; ++n) | |
{ | |
struct epoll_event epollev = events[n]; | |
struct event *ev = (struct event *)epollev.data.ptr; | |
ev->cb(ev->fd, epollev.events, ev); | |
} | |
} | |
} | |
int main() | |
{ | |
struct event_base *base = base_new(); | |
int servsock = initServSocket(base, 8080); | |
struct event *ev = event_new(base, servsock, EPOLLIN, handle_conn, NULL); | |
if (!ev) | |
onError("event_new"); | |
event_add(ev); | |
base_dispatch(base); | |
free(base); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment