Linux C簡單WebSever實現

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <ctype.h>

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LOG(fmt, ...) printf(fmt" %s:%d\n", ##__VA_ARGS__, __FILENAME__, __LINE__)
#define EXIT(error) do {perror(error); exit(EXIT_FAILURE);} while(0)

#define MAX_REQUEST_LEN 10240
#define MAX_METHOD_LEN  32
#define MAX_URI_LEN     256

int parse_request(int sockfd, char *method, char *uri) {
    char buff[MAX_REQUEST_LEN] = {0};
    ssize_t len = recv(sockfd, buff, sizeof(buff), 0);
    if (len <= 0) {
        LOG("call recv error, ret %d", (int)len);
        return -1;
    }

    char *cur = buff;
    int i = 0;
    while (i < MAX_METHOD_LEN && !isspace(*cur)) {
        method[i++] = *cur++;
    }
    method[i] = '\0';

    while(isspace(*cur)) cur++;
    i = 0;
    while (i < MAX_URI_LEN && !isspace(*cur)) {
        uri[i++] = *cur++;
    }
    uri[i] = '\0';
    return 0;
}

void unimplemented(int client) {
    char buff[] =
            "HTTP/1.0 501 Method Not Implemented\r\n"
            "Content-Type: text/html\r\n"
            "\r\n"
            "Method Not Implemented";
    send(client, buff, sizeof(buff), 0);
}

void not_found(int client)
{
    char buff[] =
            "HTTP/1.0 404 NOT FOUND\r\n"
            "Content-Type: text/html\r\n"
            "\r\n"
            "The resource specified is unavailable.\r\n";
    send(client, buff, strlen(buff), 0);
}

void url_decode(const char *src, char *dest) {
    const char *p = src;
    char code[3] = {0};
    while (*p && *p != '?') {
        if(*p == '%') {
            memcpy(code, ++p, 2);
            *dest++ = (char)strtoul(code, NULL, 16);
            p += 2;
        } else {
            *dest++ = *p++;
        }
    }
    *dest = '\0';
}

void do_get(int sockfd, const char *uri) {
    char filename[MAX_URI_LEN] = {0};
    const char *cur = uri + 1;
    size_t len = strlen(cur);
    if (len == 0) {
        strcpy(filename, "index.html");
    } else {
        url_decode(cur, filename);
    }
    printf("%s\n", filename);

    FILE *f = fopen(filename, "r");
    if (NULL == f) {
        not_found(sockfd);
        return;
    }

    char header[] =
            "HTTP/1.0 200 OK\r\n"
            "Content-Type: text/html\r\n"
            "\r\n";
    send(sockfd, header, sizeof(header), 0);

    char line[128] = {0};
    while (fgets(line, sizeof(line), f) != NULL) {
        send(sockfd, line, strlen(line), 0);
        memset(line, 0, sizeof(line));
    }
    char end[] = "\r\n";
    send(sockfd, end, 2, 0);
    fclose(f);
}

void *process(void* psockfd) {
    int sockfd = *(int*)psockfd;
    char method[MAX_METHOD_LEN] = {0};
    char uri[MAX_URI_LEN] = {0};

    if (parse_request(sockfd, method, uri) != 0)
        goto FINAL;

    if (strcmp(method, "GET") == 0) {
        do_get(sockfd, uri);
    } else {
        unimplemented(sockfd);
    }

    FINAL:
    close(sockfd);
    return NULL;
}

int create_server_fd (unsigned int port) {
    int serverfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (serverfd == -1)
        EXIT("create socket fail");

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = INADDR_ANY;

    if (bind(serverfd,(struct sockaddr *)&server, sizeof(server)) == -1)
        EXIT("bind fail");

    if (listen(serverfd, 10) == -1)
        EXIT("listen fail");

    return serverfd;
}

int main() {
    int serverfd, connfd;
    pthread_t tid;
    struct sockaddr_in client;
    socklen_t clientlen = sizeof(client);
    unsigned int port = 5000;

    serverfd = create_server_fd(port);
    LOG("Server started, listen port %d", port);
    while (1) {
        connfd = accept(serverfd, (struct sockaddr *)&client, &clientlen);
        if (pthread_create(&tid, NULL, process, &connfd) == 0) {
            unsigned char *ip = (unsigned char*)&client.sin_addr.s_addr;
            port = client.sin_port;
            LOG("request %u.%u.%u.%u:%5u", ip[0], ip[1], ip[2], ip[3], port);
        } else {
            EXIT("create thread fail");
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章