http/https 서버 구현

해결이 필요한 문제:

1. http와 리액터의 관계

2.GET

3.포스트

4.cgi

accept_cb: TCP 연결 설정

recv_cb: 클라이언트 http 요청 수락

send_cb: http 응답 보내기

실행은 순차적이며 tcp 연결의 수명 주기는 다음과 같습니다.

1.accept_cb

2.recv_cb

3.send_cb

http_request

받은 횟수가 보낸 횟수보다 적은데 이를 스티키 패킷이라고 합니다.

해결 방법: 1. 구분 기호 \r\n\r\n을 사용합니다. 2. 패키지 길이를 정의합니다.

http_response는 헤더와 본문의 두 부분으로 구성됩니다.

int filefd = open(ev->resource, O_RDONLY);
struct stat stat_buf;
fstat(filefd, &stat_buf);
close(filefd);

일반적으로 디스크 데이터를 보낼 때 디스크 데이터를 메모리로 읽어들인 후 내보내야 합니다. sendfile을 사용할 때 원칙은 mmap, 메모리 매핑을 사용하고 파일 주소를 메모리에 매핑한 다음 send를 호출하여 공간을 절약하고 복사하지 않는 것입니다. 복사본은 CPU에서 사용됩니다. sendfile은 제로 복사이기 때문에 CPU 참여가 필요하지 않습니다.

차이 경로 또는 파일: S_ISDIR(stat_buf.st_mode) S_ISREG(stat_buf.st_mode)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#include <sys/stat.h>
#include <sys/sendfile.h>



#define BUFFER_LENGTH        4096
#define MAX_EPOLL_EVENTS    1024
#define SERVER_PORT            8888
#define PORT_COUNT            1



#define HTTP_WEBSERVER_HTML_ROOT    "html"


#define HTTP_METHOD_GET        0
#define HTTP_METHOD_POST    1

typedef int NCALLBACK(int ,int, void*);

struct ntyevent {
    int fd;
    int events;
    void *arg;
    int (*callback)(int fd, int events, void *arg);
    
    int status;
    char buffer[BUFFER_LENGTH];
    int length;
    long last_active;

    // http param
    int method; //
    char resource[BUFFER_LENGTH];
    int ret_code;
    
    
};

struct eventblock {

    struct eventblock *next;
    struct ntyevent *events;
    
};

struct ntyreactor {
    int epfd;
    int blkcnt;
    struct eventblock *evblk; //fd --> 100w
};


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;
}


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;
}

int http_request(struct ntyevent *ev) {

    // GET, POST
    char linebuf[1024] = {0};
    int idx = readline(ev->buffer, 0, linebuf);

    if (strstr(linebuf, "GET")) {
        ev->method = HTTP_METHOD_GET;

        //uri
        int i = 0;
        while (linebuf[sizeof("GET ") + i] != ' ') i++;
        linebuf[sizeof("GET ")+i] = '\0';

        sprintf(ev->resource, "./%s/%s", HTTP_WEBSERVER_HTML_ROOT, linebuf+sizeof("GET "));
        
    } else if (strstr(linebuf, "POST")) {

    }

}

int recv_cb(int fd, int events, void *arg) {

    struct ntyreactor *reactor = (struct ntyreactor*)arg;
    struct ntyevent *ev = ntyreactor_idx(reactor, fd);

    int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0); // 
    
    if (len > 0) {
        
        ev->length = len;
        ev->buffer[len] = '\0';

        printf("C[%d]:%s\n", fd, ev->buffer); //http

        http_request(ev);

        //send();
        
        nty_event_del(reactor->epfd, ev);
        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);
        close(ev->fd);
        //printf("[fd=%d] pos[%ld], closed\n", fd, ev-reactor->events);
         
    } else {

        nty_event_del(reactor->epfd, ev);
        close(ev->fd);
        printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
        
    }

    return len;
}


int http_response(struct ntyevent *ev) {

    if (ev == NULL) return -1;
    memset(ev->buffer, 0, BUFFER_LENGTH);
#if 0
    const char *html = "<html><head><title>hello http</title></head><body><H1>King</H1></body></html>\r\n\r\n";
                                 
    ev->length = sprintf(ev->buffer, 
        "HTTP/1.1 200 OK\r\n\
         Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n\
         Content-Type: text/html;charset=ISO-8859-1\r\n\
         Content-Length: 83\r\n\r\n%s", 
         html);
    
#else

    printf("resource: %s\n", ev->resource);

    int filefd = open(ev->resource, O_RDONLY);
    if (filefd == -1) { // return 404

        ev->ret_code = 404;
        ev->length = sprintf(ev->buffer, 
            "HTTP/1.1 404 Not Found\r\n"
             "Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
             "Content-Type: text/html;charset=ISO-8859-1\r\n"
            "Content-Length: 85\r\n\r\n"
             "<html><head><title>404 Not Found</title></head><body><H1>404</H1></body></html>\r\n\r\n" );

    } else {


        struct stat stat_buf;
        fstat(filefd, &stat_buf);
        close(filefd);

        if (S_ISDIR(stat_buf.st_mode)) {

            
            ev->ret_code = 404;
            ev->length = sprintf(ev->buffer, 
                "HTTP/1.1 404 Not Found\r\n"
                "Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
                "Content-Type: text/html;charset=ISO-8859-1\r\n"
                "Content-Length: 85\r\n\r\n"
                "<html><head><title>404 Not Found</title></head><body><H1>404</H1></body></html>\r\n\r\n" );

        } else if (S_ISREG(stat_buf.st_mode)) {

            ev->ret_code = 200;

            ev->length = sprintf(ev->buffer, 
                "HTTP/1.1 200 OK\r\n"
                 "Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
                 "Content-Type: text/html;charset=ISO-8859-1\r\n"
                "Content-Length: %ld\r\n\r\n", 
                     stat_buf.st_size );
        }

    }

#endif
    return ev->length;
}

int send_cb(int fd, int events, void *arg) {

    struct ntyreactor *reactor = (struct ntyreactor*)arg;
    struct ntyevent *ev = ntyreactor_idx(reactor, fd);

    http_response(ev);
    //
    int len = send(fd, ev->buffer, ev->length, 0);
    if (len > 0) {
        printf("send[fd=%d], [%d]%s\n", fd, len, ev->buffer);

        if (ev->ret_code == 200) {
            int filefd = open(ev->resource, O_RDONLY);
            struct stat stat_buf;
            fstat(filefd, &stat_buf);

            sendfile(fd, filefd, NULL, stat_buf.st_size);
            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);
    
    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);

    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 fd;
}


int ntyreactor_alloc(struct ntyreactor *reactor) {

    if (reactor == NULL) return -1;
    if (reactor->evblk == NULL) return -1;

    struct eventblock *blk = reactor->evblk;
    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 ntyevents failed\n");
        return -2;
    }
    memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));

    struct eventblock *block = (struct eventblock *)malloc(sizeof(struct eventblock));
    if (block == NULL) {
        printf("ntyreactor_alloc eventblock failed\n");
        return -2;
    }
    memset(block, 0, sizeof(struct eventblock));

    block->events = evs;
    block->next = NULL;

    blk->next = block;
    reactor->blkcnt ++; //

    return 0;
}

struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd) {

    int blkidx = sockfd / MAX_EPOLL_EVENTS;

    while (blkidx >= reactor->blkcnt) {
        ntyreactor_alloc(reactor);
    }

    int i = 0;
    struct eventblock *blk = reactor->evblk;
    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("ntyreactor_alloc ntyevents failed\n");
        return -2;
    }
    memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));

    struct eventblock *block = (struct eventblock *)malloc(sizeof(struct eventblock));
    if (block == NULL) {
        printf("ntyreactor_alloc eventblock failed\n");
        return -2;
    }
    memset(block, 0, sizeof(struct eventblock));

    block->events = evs;
    block->next = NULL;

    reactor->evblk = block;
    reactor->blkcnt = 1;

    return 0;
}

int ntyreactor_destory(struct ntyreactor *reactor) {

    close(reactor->epfd);
    //free(reactor->events);

    struct eventblock *blk = reactor->evblk;
    struct eventblock *blk_next = NULL;

    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->evblk == NULL) return -1;

    //reactor->evblk->events[sockfd];
    struct ntyevent *event = ntyreactor_idx(reactor, sockfd);

    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->evblk == NULL) return -1;
    
    struct epoll_event events[MAX_EPOLL_EVENTS+1];
    
    int checkpos = 0, i;

    while (1) {
/*
        long now = time(NULL);
        for (i = 0;i < 100;i ++, checkpos ++) {
            if (checkpos == MAX_EPOLL_EVENTS) {
                checkpos = 0;
            }

            if (reactor->events[checkpos].status != 1) {
                continue;
            }

            long duration = now - reactor->events[checkpos].last_active;

            if (duration >= 60) {
                close(reactor->events[checkpos].fd);
                printf("[fd=%d] timeout\n", reactor->events[checkpos].fd);
                nty_event_del(reactor->epfd, &reactor->events[checkpos]);
            }
        }
*/

        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);
            }
            
        }

    }
}

// 3, 6w, 1, 100 == 
// <remoteip, remoteport, localip, localport>
int main(int argc, char *argv[]) {

    unsigned short port = SERVER_PORT; // listen 8888
    if (argc == 2) {
        port = atoi(argv[1]);
    }
    struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct ntyreactor));
    ntyreactor_init(reactor);

    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/https 서버 구현 - 포기 - 블로그 정원

지금까지 http/https 서버 구현에 대한 글을 소개했습니다.더 많은 관련 내용은 카이데블리온의 이전 글을 검색해 주세요.앞으로도 카이데블리온을 응원해주세요~

Supongo que te gusta

Origin blog.csdn.net/m0_58687318/article/details/126645087
Recomendado
Clasificación