程序员大本营 加入小组

519个成员 1423个话题 创建时间:2011-10-16

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

发表于 2012-04-30 3529 次查看

转自:http://blog.sina.com.cn/s/blog_7530db6f0100wh5j.html

为了更好的了解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] = '\0';
    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] == '\n' &&
        conn->request->buff[end - 2] == '\r' &&
        conn->request->buff[end - 3] == '\n' &&
        conn->request->buff[end - 4] == '\r')
        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, '\n');
    if (eol == NULL) {
        conn->stage = mickey_closing_stage;
        return;
    }
   
    if (*(eol-1) == '\r')
        *(eol-1) = '\0';
    *eol = '\0';
    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 (..)\n");
        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.\n");
        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\r\n");
    } 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\r\n");
            } 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\r\n");
                } else {
                    mickey_buffer_append(conn->response, "HTTP/1.0 200 OK\r\n");
                    sprintf(content_length, "Content-Length: %d\r\n", 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\r\n");
            } else {
                mickey_buffer_append(conn->response, "HTTP/1.0 200 OK\r\n");
                sprintf(content_length, "Content-Length: %d\r\n", stat_buf.st_size);
                mickey_buffer_append(conn->response, content_length);
            }
        } else {
            mickey_buffer_append(conn->response, "HTTP/1.0 500 Internal Server Error\r\n");
        }
    }
    mickey_buffer_append(conn->response, "\r\n");
    //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 ===\n");
   
    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 ===\n");
    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.\n");
            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.\n");
    return;
}

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

2回复
发表回复
功能维护升级中,维护完成完后将再次开放,非常抱歉给您学习造成的不便。