在上一篇文章中实现了epoll的非阻塞IO的服务端程序
可是如果实际应用中需要注册大量事件,一个超长的if代码块会严重降低代码的可读性和管理
因此参考了 Reactor模型的实现改写了epoll服务器。讲所有的事件处理函数都写成回调函数的形式。这里注意几个参数传递时候的坑。
/* Server based on EPOLL */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define BUFFER_LENGTH 1024
struct sockitem {
int epollfd;
int sockfd;
int (*callback)(int fd, int events, void* arg);
};
int recv_cb(int fd, int events, void* arg) {
if (!(events & EPOLLIN) || arg == NULL)
return -1;
printf("trigger recv_cb\n");
struct epoll_event* ev = (struct epoll_event*)arg;
struct sockitem* si = (struct sockitem*)ev->data.ptr;
int clientfd = si->sockfd;
int epoll_fd = si->epollfd;
char buffer[BUFFER_LENGTH] = { 0 };
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("read all data\n");
}
close(clientfd);
ev->events = EPOLLIN | EPOLLET;
ev->data.fd = clientfd;
ev->data.ptr = NULL;
free(si);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, ev);
}
else if (ret == 0) {
printf(" disconnect clientfd:%d\n", clientfd);
close(clientfd);
ev->events = EPOLLIN | EPOLLET;
ev->data.fd = clientfd;
ev->data.ptr = NULL;
free(si);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, ev);
return 0;
}
else {
printf("Recv: %s, %d Bytes\n", buffer, ret);
if (send(clientfd, "Hello, you are connected!\n", 26, 0)
== -1) {
// send data error maybe some network issue.
perror("send error");
close(clientfd);
}
}
}
// callback handler for accept events of the sockfd.
int accept_cb(int fd, int events, void* arg) {
if (!(events & EPOLLIN) || arg == NULL)
return -1;
printf("trigger accept_cb\n");
struct epoll_event* ev = (struct epoll_event*)arg;