• 一个简单的HTTP服务器(多线程)


    为了更好的了解HTTP协议, 特意谢了一个简单HTTP服务器, 代码只有400行. 因为很简单, 所以效率也不怎么高, 而且支持的特性也不多, 不过也可以运行, 性能跟Apache差不多.

    =============================================================================================

    #include <fcntl.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netinet/tcp.h>
    #include <errno.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define BUFFER_INIT_SIZE 512

    enum {
        mickey_reading_header_stage,
        mickey_writing_header_stage,
        mickey_writing_body_stage,
        mickey_closing_stage
    };

    enum {
        mickey_http_ok,
        mickey_http_notfound,
        mickey_http_error
    };

    enum {
        mickey_method_get,
        mickey_method_head,
        mickey_method_unknow
    };

    typedef struct {
        char *buff;
        int size;
        int free;
    } mickey_buffer_t;

    typedef struct {
        int sock;
        mickey_buffer_t *request;
        mickey_buffer_t *response;
        int keepalive;
        int method;
        mickey_buffer_t *uri;
        int status;
        int stage;
        FILE *handle;
    } mickey_connection_t;

    static int srv_sock;

    mickey_buffer_t *mickey_buffer_new() {
        mickey_buffer_t *object;
       
        object = malloc(sizeof(*object));
        if (object) {
            object->buff = malloc(BUFFER_INIT_SIZE + 1);
            if (!object->buff) {
                free(object);
                return NULL;
            }
            object->size = BUFFER_INIT_SIZE;
            object->free = BUFFER_INIT_SIZE;
        }
        return object;
    }

    int mickey_buffer_append_length(mickey_buffer_t *buf, void *data, int length) {
        int lack, need = 0;
        char *temp;
       
        if (length > buf->free) {
            lack = length - buf->free;
            while (need < lack)
                need += BUFFER_INIT_SIZE;
            temp = realloc(buf->buff, buf->size + need + 1);
            if (!temp)
                return -1;
            buf->buff = temp;
            buf->size += need;
            buf->free += need;
        }
        memcpy(buf->buff + (buf->size - buf->free), data, length);
        buf->free -= length;
        buf->buff[buf->size - buf->free] = '';
        return 0;
    }

    int mickey_buffer_append(mickey_buffer_t *buf, void *data) {
        return mickey_buffer_append_length(buf, data, strlen((char *)data));
    }

    int mickey_buffer_find_string(mickey_buffer_t *buf, char *str) {
        int idx = buf->size - buf->free;
        int slen = strlen(str);
        int i;
        for (i = 0; i < idx; i++) {
            if (idx - i >= slen) {
                if (!memcmp(buf->buff + i, str, slen)) return 1;
            } else {
                break;
            }
        }
        return 0;
    }

    int mickey_buffer_length(mickey_buffer_t *buf) {
        return buf->size - buf->free;
    }

    void mickey_buffer_print(mickey_buffer_t *buf) {
        fprintf(stderr, "%s", buf->buff);
    }

    void mickey_buffer_clean(mickey_buffer_t *buf) {
        buf->free = buf->size;
    }

    void mickey_buffer_free(mickey_buffer_t *buf) {
        if (!buf)
            return;
        if (buf->buff)
            free(buf->buff);
        free(buf);
    }

    int mickey_header_finish(mickey_connection_t *conn) {
        int end = conn->request->size - conn->request->free;
        if (conn->request->buff[end - 1] == ' ' &&
            conn->request->buff[end - 2] == ' ' &&
            conn->request->buff[end - 3] == ' ' &&
            conn->request->buff[end - 4] == ' ')
            return 1;
        return 0;
    }

    void mickey_parse_header(mickey_connection_t *conn) {
        char *eol;
        char method[16], uri[256], protocol[32];
       
        eol = strchr(conn->request->buff, ' ');
        if (eol == NULL) {
            conn->stage = mickey_closing_stage;
            return;
        }
       
        if (*(eol-1) == ' ')
            *(eol-1) = '';
        *eol = '';
        sscanf(conn->request->buff, "s %6s 2s", method, uri, protocol);
        if (!strcmp(method, "GET")) {
            conn->method = mickey_method_get;
        } else if (!strcmp(method, "HEAD")) {
            conn->method = mickey_method_head;
        } else {
            conn->method = mickey_method_unknow;
        }
        mickey_buffer_append(conn->uri, ".");
        mickey_buffer_append(conn->uri, uri);
        if (mickey_buffer_find_string(conn->uri, "..")) {
            fprintf(stderr, "[x] mickey found connection header exists (..) ");
            conn->stage = mickey_closing_stage;
        }
    }

    void connection_reading_header(mickey_connection_t *conn) {
        char buff[1024];
        int nrecv;
       
        nrecv = read(conn->sock, buff, 1024);
        if (nrecv > 0) {
            mickey_buffer_append_length(conn->request, buff, nrecv);
            if (mickey_header_finish(conn)) {
                mickey_parse_header(conn);
                conn->stage = mickey_writing_header_stage;
            }
        } else {
            fprintf(stderr, "[x] mickey cannot read data from connection. ");
            conn->stage = mickey_closing_stage;
        }
    }

    void connection_make_header(mickey_connection_t *conn) {
        struct stat stat_buf;
       
        if (stat(conn->uri->buff, &stat_buf) == -1) {
            conn->status = mickey_http_notfound;
            mickey_buffer_append(conn->response, "HTTP/1.0 404 Not Found ");
        } else {
            if (S_ISDIR(stat_buf.st_mode)) {
                mickey_buffer_append(conn->uri, "index.html");
                if (stat(conn->uri->buff, &stat_buf) == -1) {
                    conn->status = mickey_http_notfound;
                    mickey_buffer_append(conn->response, "HTTP/1.0 404 Not Found ");
                } else {
                    char content_length[256];
                   
                    conn->handle = fopen(conn->uri->buff, "r");
                    if (!conn->handle) {
                        mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error ");
                    } else {
                        mickey_buffer_append(conn->response, "HTTP/1.0 200 OK ");
                        sprintf(content_length, "Content-Length: %d ", stat_buf.st_size);
                        mickey_buffer_append(conn->response, content_length);
                    }
                }
            } else if (S_ISREG(stat_buf.st_mode)) {
                char content_length[256];
               
                conn->handle = fopen(conn->uri->buff, "r");
                if (!conn->handle) {
                    mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error ");
                } else {
                    mickey_buffer_append(conn->response, "HTTP/1.0 200 OK ");
                    sprintf(content_length, "Content-Length: %d ", stat_buf.st_size);
                    mickey_buffer_append(conn->response, content_length);
                }
            } else {
                mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error ");
            }
        }
        mickey_buffer_append(conn->response, " ");
        //mickey_buffer_print(conn->response);
    }

    void connection_send_header(mickey_connection_t *conn) {
        int bytes = mickey_buffer_length(conn->response);
        int nsend, i = 0;
       
        while (i < bytes) {
            nsend = write(conn->sock, conn->response->buff + i, bytes - i);
            if (nsend > 0) {
                i += nsend;
            } else {
                sleep(1);
                continue;
            }
        }
        conn->stage = mickey_writing_body_stage;
    }

    void connection_send_body(mickey_connection_t *conn) {
        char buff[1024];
        int bytes;
       
        if (!conn->handle) {
            conn->stage = mickey_closing_stage;
            return;
        }
       
        while (!feof(conn->handle)) {
            int ws = 0;
            int hs = 0;
           
            fread(buff, 1024, 1, conn->handle);
            bytes = strlen(buff);
            while (ws < bytes) {
                hs = write(conn->sock, buff + ws, bytes - ws);
                if (hs > 0) {
                    ws += hs;
                } else {
                    sleep(1);
                    continue;
                }
            }
        }
        fclose(conn->handle);
        conn->stage = mickey_closing_stage;
    }

    mickey_connection_t *connection_new(int sock) {
        mickey_connection_t *conn;
       
        conn = malloc(sizeof(*conn));
        if (conn) {
            conn->sock      = sock;
            conn->keepalive = 0;
            conn->stage     = mickey_reading_header_stage;
            conn->status    = mickey_http_ok;
            conn->request   = mickey_buffer_new();
            conn->response  = mickey_buffer_new();
            conn->uri       = mickey_buffer_new();
            if (!conn->request || !conn->response || !conn->uri) {
                mickey_buffer_free(conn->request);
                mickey_buffer_free(conn->response);
                mickey_buffer_free(conn->uri);
                free(conn);
                return NULL;
            }
            return conn;
        }
       
        return NULL;
    }

    void connection_exit(mickey_connection_t *conn) {
        if (conn->request)
            mickey_buffer_free(conn->request);
        if (conn->response)
            mickey_buffer_free(conn->response);
        if (conn->uri)
            mickey_buffer_free(conn->uri);
        free(conn);
    }

    void *connection_thread_entrance(void *arg) {
        mickey_connection_t *conn = (mickey_connection_t *)arg;
       
        pthread_detach(pthread_self());
       
        fprintf(stderr, "[+] === mickey thread working === ");
       
        while (1) {
            switch (conn->stage) {
            case mickey_reading_header_stage:
                connection_reading_header(conn);
                break;
            case mickey_writing_header_stage:
                connection_make_header(conn);
                connection_send_header(conn);
                break;
            case mickey_writing_body_stage:
                connection_send_body(conn);
                break;
            case mickey_closing_stage:
                close(conn->sock);
                connection_exit(conn);
                goto THREAD_CLOSING;
                break;
            }
        }

    THREAD_CLOSING:
        fprintf(stderr, "[-] === mickey thread closing === ");
        pthread_exit(NULL);
    }

    int initialize_server() {
        struct sockaddr_in addr;
        struct linger ling = {0, 0};
        int flags = 1;
       
        srv_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (srv_sock == -1)
            return -1;
        setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
        setsockopt(srv_sock, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
        setsockopt(srv_sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
    #if !defined(TCP_NOPUSH)
        setsockopt(srv_sock, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
    #endif
       
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8080);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
       
        if (bind(srv_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
            return -1;
        if (listen(srv_sock, 1024) == -1)
            return -1;
        return srv_sock;
    }

    void socket_accept_connection() {
        int new_sock;
        socklen_t len;
        struct sockaddr addr;
        mickey_connection_t *new_conn;
        pthread_t tid;
       
        while (1) {
            new_sock = accept(srv_sock, &addr, &len);
            if (new_sock == -1) {
                if (errno != EAGAIN && errno != EWOULDBLOCK) {
                    continue;
                }
                goto ERROR_FLAG;
            }
            new_conn = connection_new(new_sock);
            if (!new_conn) {
                fprintf(stderr, "[x] mickey not enough momery. ");
                close(new_sock);
                goto ERROR_FLAG;
            }
            pthread_create(&tid, NULL, connection_thread_entrance, new_conn);
        }
       
    ERROR_FLAG:
        fprintf(stderr, "[x] mickey cannot accept client connection. ");
        return;
    }

    int main(int argc, char **argv) {
        chdir("./");
        if (initialize_server() == -1) {
            fprintf(stderr, "[x] mickey initialize failed. ");
            exit(1);
        }
        socket_accept_connection();
        exit(0);
    }

    0

    0

  • 相关阅读:
    企业面试题库1
    就业模拟试题_Net
    就业模拟试题_Java
    oracle创建用户
    Activity基础类
    Activity容器控件
    面试题_Java
    Activity功能控件
    获取工作流活动的返回值
    企业面试题库_数据库部分
  • 原文地址:https://www.cnblogs.com/timssd/p/4862504.html
Copyright © 2020-2023  润新知