2.1.3http/https服务器的实现

前言

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.纯内存操作

打印信息:

在这里插入图片描述

不打印信息:

在这里插入图片描述

2.html文件

在这里插入图片描述

3.图片

在这里插入图片描述

参考博客:

Linux下性能压测工具WRK的使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值