预备知识
什么是反应堆reactor?
让epoll监视的每个fd,在有事件到来的时候执行对应的回调函数。
reactor在epoll基础上多了什么好处?
- epoll是对io的管理,加上reactor后变为对事件进行管理(一个事件对应不同的回调)
- 由于sock_item封装,对未处理完的事件,可以放到独立的buffer中
- libevent就是epoll+reactor?
以下是封装的结构体:
struct ntyevent {
int fd;
int events;
void *arg;
int (*callback)(int fd, int events, void *arg);
int status;
char buffer[BUFFER_LENGTH];
char wbuffer[BUFFER_LENGTH];
int length;
int wlength;
//long last_active;
int method;
char resource[RESOURCE_LENGTH];
};
struct eventblock {
struct eventblock *next;
struct ntyevent *events;
};
struct ntyreactor {
int epfd;
int blkcnt;
struct eventblock *evblks;
};
HTTP简介(面试题)
HTTP协议服务器传输超文本到本地浏览器的传送协议。 HTTP 是一个基于TCP/IP 通信协议来传递数据( HTML 文件 , 图片文件 , 查询结果等)。
HTTP协议工作于客户端 服务端架(CS)构上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。
Web服务器有: Apache 服务器, IIS 服务器( Internet Information Services )等。
Web服务器根据接收到 的请求后,向客户端发送响应信息。
HTTP默认端口号为 80 ,但是你也可以改为 8080 或者其他端口。
注意事项
- HTTP 是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的
请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 - HTTP 是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何
类型的数据都可以通过 HTTP 发送。客户端以及服务器指定使用适合的 MIME type 内容
类型。 - HTTP 是无状态: HTTP 协议是无状态协议。无状态是指协议对于事务处 理没有记忆能力。
缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接
传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP协议通信流程
HTTP消息结构
HTTP是基于客户端 服务端( C/S )的架构模型,通过一个可靠的链接来交换信息,是一个无
状态的请求 响应协议。
一个HTTP" 客户端 是一个应用程序( Web 浏览器或其他任何客户端),通过连接到服务器达
到向服务器发送一个或多个 HTTP 的请求的目的。
一个HTTP" 服务器 同样也是一个应用程序(通常是一个 Web 服务,如 Apache Web 服务器或 IIS 服务器等),通过接收客户端的请求并向客户端发送 HTTP 响应数据。
HTTP使用统一资源标识符( Uniform Resource Identifiers, URI )来传输数据和建立连接。
一旦建立连接后,数据消息就通过类似Internet 邮件所使用的格式 [ 和多用途Internet 邮件扩展( MIME ))[ 来传送。
客户端请求消息
客户端发送一个HTTP 请求到服务器的请求消息包括以下格式:请求行( requ est line )、请求头部( header )、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
客户端请求消息
HTTP响应也由四个部分组成,分别是**:状态行、消息报头、空行和响应正文。**
程序实现关键部分
请求报文的解析
通过原来的代码获得浏览器访问服务器获得的请求数据
其中第一行的GET代表请求方法,/index.html代表请求资源的路径, HTTP/1.1代表协议。
首先了解到recv_cb函数,是可读事件发生时调用的回调函数,在这个函数里我们对接受到的http请求报文进行解析,重点就是解析出请求行中的【请求方法】和【请求资源的路径】,故我们封装一个nty_http_request
函数进行处理:
int nty_http_request(struct ntyevent *ev){
char linebuffer[1024] = {0};
//获取请求行
int idx = readline(ev->buffer, 0, linebuffer);
//查找在linebuffer里"GET"第一次出现的下标
if(strstr(linebuffer, "GET")) {
ev->method = HTTP_METHOD_GET; //1.解析出 【请求方法】
//解析出请求行中的资源路径
int i = 0;
//sizeof("GET ") 结果为5,结尾\0也算一个
while(linebuffer[sizeof("GET ") + i] != ' ') i++;
linebuffer[sizeof("GET ") + i] = '\0';
//2.解析出请求资源路径
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;
}
}
对http请求组织响应报文(response)
当有事件可写的时候,表示收到请求报文,我们要发出响应报文给对端,则会调用函数send_cb
,其中一个关键步骤就是组织好即将要发送的报文
发送的是html文件的响应报文header应该为
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: 79\r\n"
"Content-Type: text/html\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n"
"<html><head><title>ynufe.jyh</title></head><body><h1>jyh</h1></body></html>\r\n");
若发送的是图片文件的响应报文header应该为
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n", stat_buf.st_size);
//组织响应正文前的内容header
int nty_http_response_get_method(struct 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"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: 79\r\n"
"Content-Type: text/html\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n"
"<html><head><title>ynufe.jyh</title></head><body><h1>jyh</h1></body></html>\r\n");
ev->wlength = len;
}else{
//struct stat 是描述一个linux系统文件系统中的文件属性的结构
struct stat stat_buf;
fstat(filefd, &stat_buf); //将filefd对应文件的信息写入stat_buf
close(filefd);
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n", stat_buf.st_size);
ev->wlength = len;
}
return len;
}
//取出资源,然后sendcb发送该资源
int nty_http_response(struct ntyevent *ev){
//ev->method, ev-resources
if(ev->method == HTTP_METHOD_GET){
return nty_http_response_get_method(ev);
}else if(ev->method == HTTP_METHOD_POST){
}
}
组织响应报文的格式一定要正确,比方说响应正文前面是有一个空行,所以在正文之前的有内容的行最后是\r\n\r\n
不然浏览器解析的时候就会提示
ERR_INVALID_HTTP_RESPONSE
send_cb
函数如下:
int send_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL) return -1;
nty_http_response(ev); //encode
//发送header(状态行、消息报头、空行)
int len = send(fd, ev->wbuffer, ev->wlength, 0);
if (len > 0) {
printf("resource:%s\n",ev->resource);
//打开文件,并发送body(响应正文)
int filefd = open(ev->resource, O_RDONLY);
struct stat stat_buf;
fstat(filefd, &stat_buf);
//设置回阻塞
int flag = fcntl(fd, F_GETFL, 0);
//O_NONBLOCK是 0000....100000,
//故~O_NONBLOCK就是1111....011111,就把NONBLOCK的那一位置零了
flag &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
//fd为发送的通道,filefd为发送的文件
//这是一个要求阻塞的函数,但是我们的fd是非阻塞的,(不设置阻塞其实也可以)
int ret = sendfile(fd, filefd, NULL, stat_buf.st_size);
if(ret == -1) {
printf("sendfile: errno: %d\n",errno);
}
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
close(filefd);
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;
}
sendfile()效率比 read() + write()要高,因为它采用了零拷贝,直接在内核态就将文件fd对应的缓冲区数据传给socketfd的发送缓冲区。 mmap
多输出一条语句(如printf),会导致web服务器连接数的效率急剧下降
上面红圈是有printf测试的结果,下面红圈是没有printf测试结果,可以看出性能差距高达2倍以上。可以看出io操作非常费时。
设置端口复用
有时候我们主动服务器停止服务器,造成连接断开,由于2msl 的 time_wait状态,导致端口仍没有释放,我们可以在监听fd绑定地址端口之前设置端口复用:
int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENTS 1024
#define RESOURCE_LENGTH 1024
#define SERVER_PORT 8888
#define PORT_COUNT 1
typedef int NCALLBACK(int ,int, void*);
#define HTTP_METHOD_GET 0
#define HTTP_METHOD_POST 1
#define HTTP_WEB_ROOT "/home/jyhlinux/share/2.1.3_http_server/html"
struct ntyevent {
int fd;
int events;
void *arg;
int (*callback)(int fd, int events, void *arg);
int status;
char buffer[BUFFER_LENGTH];
char wbuffer[BUFFER_LENGTH];
int length;
int wlength;
//long last_active;
int method;
char resource[RESOURCE_LENGTH];
};
struct eventblock {
struct eventblock *next;
struct ntyevent *events;
};
struct ntyreactor {
int epfd;
int blkcnt;
struct eventblock *evblks;
};
int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd);
void nty_event_set(struct 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 ;
}
int nty_event_add(int epfd, int events, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
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;
}
int nty_event_del(int epfd, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
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;
}
//读取allbuf一行到linebuf中,返回下一行的起始下标
int readline(char *allbuf, int idx, char *linebuf){
int len = strlen(allbuf);
for(;idx < len; ++idx){
//\r\n表示换行
if(allbuf[idx] == 'r' && allbuf[idx + 1] == '\n')
return idx + 2;
else
*(linebuf++) = allbuf[idx];
}
return -1;
}
//对请求进行处理
int nty_http_request(struct ntyevent *ev){
char linebuffer[1024] = {0};
//获取请求行
int idx = readline(ev->buffer, 0, linebuffer);
//查找在linebuffer里"GET"第一次出现的下标
if(strstr(linebuffer, "GET")) {
ev->method = HTTP_METHOD_GET;
//printf("sizeof(\"GET \") : %ld\n", sizeof("GET "));
//解析出请求行中的资源路径
int i = 0;
//sizeof("GET ") 结果为5,结尾\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;
}
}
int nty_http_response_get_method(struct ntyevent *ev){
#if 0
int len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: 79\r\n"
"Content-Type: text/html\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n"
"<html><head><title>ynufe.jyh</title></head><body><h1>jyh</h1></body></html>\r\n");
ev->wlength = len;
#else
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"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: 79\r\n"
"Content-Type: text/html\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n"
"<html><head><title>ynufe.jyh</title></head><body><h1>jyh</h1></body></html>\r\n");
ev->wlength = len;
}else{
//struct stat 是描述一个linux系统文件系统中的文件属性的结构
struct stat stat_buf;
fstat(filefd, &stat_buf);
close(filefd);
#endif
#if 0
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n", stat_buf.st_size);
#else //发图片
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform\r\n"
"Connection: keep-alive\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n"
"Date: Fri, 07 Oct 2022 12:52:22 GMT\r\n\r\n", stat_buf.st_size);
ev->wlength = len;
}
#endif
return len;
}
//取出资源,然后sendcb发送该资源
int nty_http_response(struct ntyevent *ev){
//ev->method, ev-resources
if(ev->method == HTTP_METHOD_GET){
return nty_http_response_get_method(ev);
}else if(ev->method == HTTP_METHOD_POST){
}
}
int recv_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL) return -1;
int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);
nty_event_del(reactor->epfd, ev);
if (len > 0) {
ev->length = len;
ev->buffer[len] = '\0';
//printf("recv [%d]:%s\n", fd, ev->buffer);
nty_http_request(ev); //!!!parser http header
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));
}
return len;
}
int send_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL) return -1;
nty_http_response(ev); //encode获取待发送的资源
int len = send(fd, ev->wbuffer, ev->wlength, 0);
if (len > 0) {
printf("resource:%s\n",ev->resource);
//打开文件,并发送文件
int filefd = open(ev->resource, O_RDONLY);
struct stat stat_buf;
fstat(filefd, &stat_buf);
//设置回阻塞
int flag = fcntl(fd, F_GETFL, 0);
flag &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
//fd为发送的通道,filefd为发送的文件
//这是一个要求阻塞的函数,但是我们的fd是非阻塞的,(不设置阻塞其实也可以)
int ret = sendfile(fd, filefd, NULL, stat_buf.st_size);
if(ret == -1) {
printf("sendfile: errno: %d\n",errno);
}
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
close(filefd);
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;
}
int accept_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
if (reactor == NULL) return -1;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int clientfd;
if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) {
if (errno != EAGAIN && errno != EINTR) {
}
printf("accept: %s\n", strerror(errno));
return -1;
}
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;
}
struct ntyevent *event = ntyreactor_idx(reactor, clientfd);
if (event == NULL) return -1;
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;
}
int init_sock(short port) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
int opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
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 ntyreactor_alloc(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
if (reactor->evblks == NULL) return -1;
struct eventblock *blk = reactor->evblks;
while (blk->next != NULL) {
blk = blk->next;
}
struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
if (evs == NULL) {
printf("ntyreactor_alloc ntyevent failed\n");
return -2;
}
memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL) {
printf("ntyreactor_alloc eventblock failed\n");
return -3;
}
block->events = evs;
block->next = NULL;
blk->next = block;
reactor->blkcnt ++;
return 0;
}
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd) {
if (reactor == NULL) return NULL;
if (reactor->evblks == NULL) return NULL;
int blkidx = sockfd / MAX_EPOLL_EVENTS;
while (blkidx >= reactor->blkcnt) {
ntyreactor_alloc(reactor);
}
int i = 0;
struct eventblock *blk = reactor->evblks;
while (i++ != blkidx && blk != NULL) {
blk = blk->next;
}
return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}
int ntyreactor_init(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
memset(reactor, 0, sizeof(struct ntyreactor));
reactor->epfd = epoll_create(1);
if (reactor->epfd <= 0) {
printf("create epfd in %s err %s\n", __func__, strerror(errno));
return -2;
}
struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
if (evs == NULL) {
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epfd);
return -3;
}
memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL) {
free(evs);
close(reactor->epfd);
return -3;
}
block->events = evs;
block->next = NULL;
reactor->evblks = block;
reactor->blkcnt = 1;
return 0;
}
int ntyreactor_destory(struct ntyreactor *reactor) {
close(reactor->epfd);
struct eventblock *blk = reactor->evblks;
struct eventblock *blk_next;
while (blk != NULL) {
blk_next = blk->next;
free(blk->events);
free(blk);
blk = blk_next;
}
return 0;
}
int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor) {
if (reactor == NULL) return -1;
if (reactor->evblks == NULL) return -1;
struct ntyevent *event = ntyreactor_idx(reactor, sockfd);
if (event == NULL) return -1;
nty_event_set(event, sockfd, acceptor, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
return 0;
}
int ntyreactor_run(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
if (reactor->epfd < 0) return -1;
if (reactor->evblks == NULL) 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 ++) {
struct ntyevent *ev = (struct ntyevent*)events[i].data.ptr;
if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {
ev->callback(ev->fd, events[i].events, ev->arg);
}
if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {
ev->callback(ev->fd, events[i].events, ev->arg);
}
}
}
}
int main(int argc, char *argv[]) {
struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct 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;
}
杂项
-
因为io类型不同(磁盘io、网络io、定时器io)所以,回调函数对每一个fd可能要单独写
-
errno 9 (EBADF Bad file descriptor),可能是filefd已被close
进阶&问题
- 阅读reactor代码,练习手撕reactor?
- recv_cb和send_cb能拆分成几个阶段
- 在多个线程同时调用epoll_wait(epfd)会惊群,要用oneshot解决