前言
reactor在epoll的基础上,有哪些好处?
-
epoll对io进行管理,reactor对事件进行管理
-
由于ntyevent封装,对未处理完的事件放到一个独立的buffer里
TCP服务器
本文基于以下代码实现http服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 9999
#define PORT_COUNT 1
typedef int NCALLBACK(int, int, void*);
typedef struct ntyevent
{
int fd;
int events; // EPOLLIN, EPOLLOUT等
void* arg;
int (*callback)(int fd, int events, void* arg);
int status; // 0代表fd未被epoll监听,1代表fd已被epoll监听
char rbuffer[BUFFER_LENGTH];
char wbuffer[BUFFER_LENGTH];
int rlength;
int wlength;
// long last_active;
}ntyevent;
typedef struct eventblock
{
struct ntyevent* events;
struct eventblock* next;
}eventblock;
// 反应堆,监听事件,管理fd相关内容(查找fd对应的ntyevent等)
typedef struct ntyreactor
{
int epfd;
int blkcnt;
struct eventblock* evblks;
}ntyreactor;
int recv_cb(int fd, int events, void* arg);
int send_cb(int fd, int events, void* arg);
// --- ntyevent相关操作
// 设置ntyevent
void nty_event_set(ntyevent* ev, int fd, NCALLBACK callback, void* arg)
{
ev->fd = fd;
ev->callback = callback;
ev->events = 0;
ev->arg = arg;
// ev->last_active = time(NULL);
return;
}
// 将ntyevent对应fd及事件添加/修改到epoll监听列表
int nty_event_add(int epfd, int events, ntyevent* ev)
{
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
// 判断fd是否已经被epoll监听
// 若是,则修改该fd被监听的事件,否则,将该fd添加到epoll监听列表
if(ev->status == 1)
{
op = EPOLL_CTL_MOD;
}
else
{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if(epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0)
{
printf("event add failed [fd=%d], events[%d]\n",ev->fd, events);
return -1;
}
return 0;
}
// 将ntyevent对应fd及事件从epoll监听列表中删除
int nty_event_del(int epfd, ntyevent* ev)
{
struct epoll_event ep_ev = {0, {0}};
// 若fd未被添加到epoll监听列表
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;
}
// ---
// --- ntyreactor相关操作
// 初始化ntyreactor
int ntyreactor_init(ntyreactor* reactor)
{
if(!reactor) return -1;
memset(reactor, 0, sizeof(ntyreactor));
reactor->epfd = epoll_create(1);
if(reactor->epfd <= 0)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
return -2;
}
ntyevent* evs = (ntyevent*)malloc(MAX_EPOLL_EVENTS*sizeof(ntyevent));
if(!evs)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epfd);
return -3;
}
memset(evs, 0, (MAX_EPOLL_EVENTS)*sizeof(ntyevent));
eventblock* block = (eventblock*)malloc(sizeof(eventblock));
if(!block)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
free(evs);
close(reactor->epfd);
return -3;
}
block->events = evs;
block->next = NULL;
reactor->evblks = block;
reactor->blkcnt = 1;
return 0;
}
// 析构ntyreactor
int ntyreactor_destory(ntyreactor* reactor)
{
close(reactor->epfd);
eventblock* blk = reactor->evblks;
eventblock* blk_next;
while(blk)
{
blk_next = blk->next;
free(blk->events);
free(blk);
blk = blk_next;
}
return 0;
}
// 为ntyreactor的链表(evblks)创建一个新结点,并分配内存
int ntyreactor_alloc(ntyreactor* reactor)
{
if(!reactor) return -1;
if(!reactor->evblks) return -1;
eventblock* blk = reactor->evblks;
while(blk->next)
blk = blk->next;
ntyevent* evs = (ntyevent*)malloc((MAX_EPOLL_EVENTS)*sizeof(ntyevent));
if(!evs)
{
printf("%s ntyevent failed\n", __func__);
return -2;
}
memset(evs, 0, (MAX_EPOLL_EVENTS)*sizeof(ntyevent));
eventblock* block = (eventblock*)malloc(sizeof(eventblock));
if(!block)
{
printf("%s eventblock failed\n", __func__);
free(evs);
return -3;
}
block->events = evs;
block->next = NULL;
blk->next = block;
++(reactor->blkcnt);
return 0;
}
// 在ntyreactor中查找sockfd对应的ntyevent
ntyevent* ntyreactor_idx(ntyreactor* reactor, int sockfd)
{
if(!reactor) return NULL;
if(!reactor->evblks) return NULL;
int blkidx = sockfd / MAX_EPOLL_EVENTS;
while(blkidx >= reactor->blkcnt)
{
ntyreactor_alloc(reactor);
}
int i = 0;
eventblock* blk = reactor->evblks;
while(i++ != blkidx && blk)
{
blk = blk->next;
}
return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}
// 向ntyreactor添加listenfd
int ntyreactor_addlistener(ntyreactor* reactor, int sockfd, NCALLBACK* acceptor)
{
if(!reactor) return -1;
if(!reactor->evblks) return -1;
ntyevent* event = ntyreactor_idx(reactor, sockfd);
if(!event) return -1;
nty_event_set(event, sockfd, acceptor, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
return 0;
}
// 运行ntyreactor,即循环执行epoll_wait,对可读可写IO执行相应回调函数
int ntyreactor_run(ntyreactor* reactor)
{
if(!reactor) return -1;
if(reactor->epfd < 0) return -1;
if(!reactor->evblks) return -1;
struct epoll_event events[MAX_EPOLL_EVENTS+1];
int checkpos = 0, i;
while(1)
{
int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
if(nready < 0)
{
printf("epoll_wait error, exit\n");
continue;
}
for(i = 0;i < nready; i++)
{
ntyevent* ev = (ntyevent*)events[i].data.ptr;
if((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) // 若IO可输入
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) // 若IO可输出
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
}
}
}
// ---
// --- 回调函数
// recv回调函数
int recv_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
ntyevent* ev = ntyreactor_idx(reactor, fd);
if(!ev) return -1;
int len = recv(fd, ev->rbuffer, BUFFER_LENGTH, 0);
nty_event_del(reactor->epfd, ev);
if(len > 0) // 已接收数据
{
ev->rlength = len;
ev->rbuffer[len] = '\0'; // 去除脏数据
printf("recv [%d]:%s\n", fd, ev->rbuffer);
// 接收数据后,将fd设置为待发送模式
nty_event_set(ev, fd, send_cb, reactor);
nty_event_add(reactor->epfd, EPOLLOUT, ev);
}
else if(len == 0) // 客户端断开连接
{
nty_event_del(reactor->epfd, ev);
printf("recv_cb --> disconnect\n");
close(ev->fd);
}
else // 发生异常
{
if(errno == EAGAIN && errno == EWOULDBLOCK)
{
}
else if(errno == ECONNRESET)
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
// strerror() 获取指向错误消息字符串的指针
}
return len;
}
// send回调函数
int send_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
ntyevent* ev = ntyreactor_idx(reactor, fd);
if(!ev) return -1;
// 从rbuffer复制数据到wbuffer,再进行发送
memcpy(ev->wbuffer, ev->rbuffer, ev->rlength);
ev->wlength = ev->rlength;
int len = send(fd, ev->wbuffer, ev->wlength, 0);
if(len > 0) // 已发送数据
{
printf("send[fd=%d], [%d]%s\n",fd, len, ev->wbuffer);
// 发送数据后,将fd设置为待接收模式
nty_event_del(reactor->epfd, ev);
nty_event_set(ev, fd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, ev);
}
else // 发送失败
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
printf("send[fd=%d] error %s\n", fd, strerror(errno));
}
return len;
}
// accept回调函数
int accept_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
if(!reactor) return -1;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int clientfd = 0;
if((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1)
{
if(errno != EAGAIN && errno != EINTR)
{
}
printf("accept[fd=%d] error %s\n", fd, strerror(errno));
return -1;
}
// 将clientfd设置为非阻塞
int flag = 0;
if((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0)
{
printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
return -1;
}
ntyevent* event = ntyreactor_idx(reactor, clientfd);
if(!event) return -1;
// 初始化客户端fd为接收模式
nty_event_set(event, clientfd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
printf("new connect [%s:%d], pos[%d]\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);
return 0;
}
// ---
// 创建服务器端口(listenfd)
int init_sock(short port)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(listen(fd, 20) < 0)
{
printf("listen failed : %s\n", strerror(errno));
return -1;
}
printf("listen server port : %d\n", port);
return fd;
}
int main(int argc, char* argv[])
{
ntyreactor* reactor = (ntyreactor*)malloc(sizeof(ntyreactor));
ntyreactor_init(reactor);
unsigned short port = SERVER_PORT;
if(argc == 2)
{
port = atoi(argv[1]);
}
int i = 0;
int sockfds[PORT_COUNT] = {0};
for(i = 0; i < PORT_COUNT; i++)
{
sockfds[i] = init_sock(port + i);
ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
}
ntyreactor_run(reactor);
ntyreactor_destory(reactor);
for(i = 0; i < PORT_COUNT; i++)
{
close(sockfds[i]);
}
free(reactor);
return 0;
}
http服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 9999
#define PORT_COUNT 1
typedef int NCALLBACK(int, int, void*);
// --- http
#include <sys/stat.h>
#define RESOURCE_LENGTH 1024
#define HTTP_METHOD_GET 0
#define HTTP_METHOD_POST 1
// html文件,图片路径
#define HTTP_WEB_ROOT "/home/lewin/share/Linux_Project/2.1.3_http,https_server/html"
#define HTTPRESPONSE_HTML 1
#define HTTPRESPONSE_IMAGE 0
// ---
typedef struct ntyevent
{
int fd;
int events; // EPOLLIN, EPOLLOUT等
void* arg;
int (*callback)(int fd, int events, void* arg);
int status; // 0代表fd未被epoll监听,1代表fd已被epoll监听
char rbuffer[BUFFER_LENGTH];
char wbuffer[BUFFER_LENGTH];
int rlength;
int wlength;
// long last_active;
// http request
int method;
char resource[RESOURCE_LENGTH];
int hasfile;
}ntyevent;
typedef struct eventblock
{
struct ntyevent* events;
struct eventblock* next;
}eventblock;
// 反应堆,监听事件,管理fd相关内容(查找fd对应的ntyevent等)
typedef struct ntyreactor
{
int epfd;
int blkcnt;
struct eventblock* evblks;
}ntyreactor;
int recv_cb(int fd, int events, void* arg);
int send_cb(int fd, int events, void* arg);
// --- ntyevent相关操作
// 设置ntyevent
void nty_event_set(ntyevent* ev, int fd, NCALLBACK callback, void* arg)
{
ev->fd = fd;
ev->callback = callback;
ev->events = 0;
ev->arg = arg;
// ev->last_active = time(NULL);
ev->hasfile = 0;
return;
}
// 将ntyevent对应fd及事件添加/修改到epoll监听列表
int nty_event_add(int epfd, int events, ntyevent* ev)
{
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
// 判断fd是否已经被epoll监听
// 若是,则修改该fd被监听的事件,否则,将该fd添加到epoll监听列表
if(ev->status == 1)
{
op = EPOLL_CTL_MOD;
}
else
{
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if(epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0)
{
printf("event add failed [fd=%d], events[%d]\n",ev->fd, events);
return -1;
}
return 0;
}
// 将ntyevent对应fd及事件从epoll监听列表中删除
int nty_event_del(int epfd, ntyevent* ev)
{
struct epoll_event ep_ev = {0, {0}};
// 若fd未被添加到epoll监听列表
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;
}
// ---
// --- ntyreactor相关操作
// 初始化ntyreactor
int ntyreactor_init(ntyreactor* reactor)
{
if(!reactor) return -1;
memset(reactor, 0, sizeof(ntyreactor));
reactor->epfd = epoll_create(1);
if(reactor->epfd <= 0)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
return -2;
}
ntyevent* evs = (ntyevent*)malloc(MAX_EPOLL_EVENTS*sizeof(ntyevent));
if(!evs)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epfd);
return -3;
}
memset(evs, 0, (MAX_EPOLL_EVENTS)*sizeof(ntyevent));
eventblock* block = (eventblock*)malloc(sizeof(eventblock));
if(!block)
{
printf("create epfd in %s err %s\n", __func__, strerror(errno));
free(evs);
close(reactor->epfd);
return -3;
}
block->events = evs;
block->next = NULL;
reactor->evblks = block;
reactor->blkcnt = 1;
return 0;
}
// 析构ntyreactor
int ntyreactor_destory(ntyreactor* reactor)
{
close(reactor->epfd);
eventblock* blk = reactor->evblks;
eventblock* blk_next;
while(blk)
{
blk_next = blk->next;
free(blk->events);
free(blk);
blk = blk_next;
}
return 0;
}
// 为ntyreactor的链表(evblks)创建一个新结点,并分配内存
int ntyreactor_alloc(ntyreactor* reactor)
{
if(!reactor) return -1;
if(!reactor->evblks) return -1;
eventblock* blk = reactor->evblks;
while(blk->next)
blk = blk->next;
ntyevent* evs = (ntyevent*)malloc((MAX_EPOLL_EVENTS)*sizeof(ntyevent));
if(!evs)
{
printf("%s ntyevent failed\n", __func__);
return -2;
}
memset(evs, 0, (MAX_EPOLL_EVENTS)*sizeof(ntyevent));
eventblock* block = (eventblock*)malloc(sizeof(eventblock));
if(!block)
{
printf("%s eventblock failed\n", __func__);
free(evs);
return -3;
}
block->events = evs;
block->next = NULL;
blk->next = block;
++(reactor->blkcnt);
return 0;
}
// 在ntyreactor中查找sockfd对应的ntyevent
ntyevent* ntyreactor_idx(ntyreactor* reactor, int sockfd)
{
if(!reactor) return NULL;
if(!reactor->evblks) return NULL;
int blkidx = sockfd / MAX_EPOLL_EVENTS;
while(blkidx >= reactor->blkcnt)
{
ntyreactor_alloc(reactor);
}
int i = 0;
eventblock* blk = reactor->evblks;
while(i++ != blkidx && blk)
{
blk = blk->next;
}
return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}
// 向ntyreactor添加listenfd
int ntyreactor_addlistener(ntyreactor* reactor, int sockfd, NCALLBACK* acceptor)
{
if(!reactor) return -1;
if(!reactor->evblks) return -1;
ntyevent* event = ntyreactor_idx(reactor, sockfd);
if(!event) return -1;
nty_event_set(event, sockfd, acceptor, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
return 0;
}
// 运行ntyreactor,即循环执行epoll_wait,对可读可写IO执行相应回调函数
int ntyreactor_run(ntyreactor* reactor)
{
if(!reactor) return -1;
if(reactor->epfd < 0) return -1;
if(!reactor->evblks) return -1;
struct epoll_event events[MAX_EPOLL_EVENTS+1];
int checkpos = 0, i;
while(1)
{
int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
if(nready < 0)
{
printf("epoll_wait error, exit\n");
continue;
}
for(i = 0;i < nready; i++)
{
ntyevent* ev = (ntyevent*)events[i].data.ptr;
if((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) // 若IO可输入
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) // 若IO可输出
{
ev->callback(ev->fd, events[i].events, ev->arg);
}
}
}
}
// ---
// --- http相关操作
// 读取一行
int readline(char* allbuf, int idx, char* linebuf)
{
int len = strlen(allbuf);
for(;idx < len;idx++)
{
if(allbuf[idx] == '\r' && allbuf[idx+1] == '\n')
return idx + 2;
else
*(linebuf++) = allbuf[idx];
}
return -1;
}
// 解析http请求
int nty_http_request(ntyevent* ev)
{
char linebuffer[BUFFER_LENGTH] = {0};
int idx = readline(ev->rbuffer, 0, linebuffer);
if(strstr(linebuffer, "GET")) // 若linebuffer含有"GET",为http get请求
{
ev->method = HTTP_METHOD_GET;
int i = 0;
while(linebuffer[sizeof("GET ") + i] != ' ') ++i;
linebuffer[sizeof("GET ") + i] = '\0';
sprintf(ev->resource, "%s/%s", HTTP_WEB_ROOT, linebuffer+sizeof("GET "));
//printf("resource: %s\n", ev->resource);
}
else if(strstr(linebuffer, "POST"))
{
ev->method = HTTP_METHOD_POST;
}
//printf("line: %s\n", linebuffer);
}
// 响应http get请求
int nty_http_response_get_method(ntyevent* ev)
{
int filefd = open(ev->resource, O_RDONLY); // 打开文件
int len = 0;
if(filefd == -1) // 若文件不存在
{
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 79\r\n"
"Content-Type: text/html\r\n" // 内容类型:html文件
"Date: Sat, 15 Oct 2022 15:08:11 GMT\r\n\r\n"
"<html><head><title>No File</title></head><body><h1>No File</h1><body/></html>"
);
ev->wlength = len;
}
else // 若文件存在
{
ev->hasfile = 1;
struct stat stat_buf;
fstat(filefd, &stat_buf); // 获取文件信息
close(filefd);
#if HTTPRESPONSE_HTML
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n" // 内容类型为html
"Date: Sat, 15 Oct 2022 15:08:11 GMT\r\n\r\n",
stat_buf.st_size
);
#elif HTTPRESPONSE_IMAGE
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n" // 内容类型为image
"Date: Sat, 15 Oct 2022 15:08:11 GMT\r\n\r\n",
stat_buf.st_size
);
#endif
ev->wlength = len;
}
return len;
}
int nty_http_response(ntyevent* ev)
{
if(ev->method == HTTP_METHOD_GET)
{
return nty_http_response_get_method(ev);
}
else if(ev->method == HTTP_METHOD_GET)
{
}
return -1;
}
// ---
// --- 回调函数
// recv回调函数
int recv_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
ntyevent* ev = ntyreactor_idx(reactor, fd);
if(!ev) return -1;
int len = recv(fd, ev->rbuffer, BUFFER_LENGTH, 0);
nty_event_del(reactor->epfd, ev);
if(len > 0) // 已接收数据
{
ev->rlength = len;
ev->rbuffer[len] = '\0'; // 去除脏数据
//printf("recv [%d]:%s\n", fd, ev->rbuffer);
// --- http
nty_http_request(ev); // parser http hdr
// ---
// 接收数据后,将fd设置为待发送模式
nty_event_set(ev, fd, send_cb, reactor);
nty_event_add(reactor->epfd, EPOLLOUT, ev);
}
else if(len == 0) // 客户端断开连接
{
nty_event_del(reactor->epfd, ev);
printf("recv_cb --> disconnect\n");
close(ev->fd);
}
else // 发生异常
{
if(errno == EAGAIN && errno == EWOULDBLOCK)
{
}
else if(errno == ECONNRESET)
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
// strerror() 获取指向错误消息字符串的指针
}
return len;
}
// send回调函数
int send_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
ntyevent* ev = ntyreactor_idx(reactor, fd);
if(!ev) return -1;
// --- http
int ret = nty_http_response(ev); // encode
// ---
if(ret == -1) // 非http请求
{
// 从rbuffer复制数据到wbuffer,再进行发送
memcpy(ev->wbuffer, ev->rbuffer, ev->rlength);
ev->wlength = ev->rlength;
}
int len = send(fd, ev->wbuffer, ev->wlength, 0);
if(len > 0) // 已发送数据
{
//printf("send[fd=%d], [%d]%s\n",fd, len, ev->wbuffer);
// --- http
//printf("resource: %s\n", ev->resource);
if(ev->hasfile)
{
int filefd = open(ev->resource, O_RDONLY);
// if(filefd < 0) return -1
struct stat stat_buf;
fstat(filefd, &stat_buf);
// - 设为阻塞IO
int flag = fcntl(fd, F_GETFL, 0);
flag &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
// -
// 发送文件
int ret = sendfile(fd, filefd, NULL, stat_buf.st_size);
if(ret == -1)
printf("sendfile: errno: %d\n", errno);
// - 设回非阻塞IO
flag = fcntl(fd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
// -
close(filefd);
}
// ---
// 发送数据后,将fd设置为待接收模式
nty_event_del(reactor->epfd, ev);
nty_event_set(ev, fd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, ev);
}
else // 发送失败
{
nty_event_del(reactor->epfd, ev);
close(ev->fd);
printf("send[fd=%d] error %s\n", fd, strerror(errno));
}
return len;
}
// accept回调函数
int accept_cb(int fd, int events, void* arg)
{
ntyreactor* reactor = (ntyreactor*)arg;
if(!reactor) return -1;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int clientfd = 0;
if((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1)
{
if(errno != EAGAIN && errno != EINTR)
{
}
printf("accept[fd=%d] error %s\n", fd, strerror(errno));
return -1;
}
// 将clientfd设置为非阻塞
int flag = 0;
if((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0)
{
printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
return -1;
}
ntyevent* event = ntyreactor_idx(reactor, clientfd);
if(!event) return -1;
// 初始化客户端fd为接收模式
nty_event_set(event, clientfd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
printf("new connect [%s:%d], pos[%d]\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);
return 0;
}
// ---
// 创建服务器端口(listenfd)
int init_sock(short port)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(listen(fd, 20) < 0)
{
printf("listen failed : %s\n", strerror(errno));
return -1;
}
printf("listen server port : %d\n", port);
return fd;
}
int main(int argc, char* argv[])
{
ntyreactor* reactor = (ntyreactor*)malloc(sizeof(ntyreactor));
ntyreactor_init(reactor);
unsigned short port = SERVER_PORT;
if(argc == 2)
{
port = atoi(argv[1]);
}
int i = 0;
int sockfds[PORT_COUNT] = {0};
for(i = 0; i < PORT_COUNT; i++)
{
sockfds[i] = init_sock(port + i);
ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
}
ntyreactor_run(reactor);
ntyreactor_destory(reactor);
for(i = 0; i < PORT_COUNT; i++)
{
close(sockfds[i]);
}
free(reactor);
return 0;
}
性能测试
使用wrk测试:
-
安装wrk(Ubuntu)
-
sudo apt-get install build-essential libssl-dev git -y git clone https://github.com/wg/wrk.git ./ make
-
-
执行命令
-
./wrk -c 100 -d 30s -t 10 http://192.168.210.132:9999/lewin.html ./wrk -c 100 -d 30s -t 10 http://192.168.210.132:9999/lewin.png
-
1.纯内存操作
打印信息:
不打印信息: