使用多线程,在C中构建一个简单的HTTP服务器

时间:2015-12-02 21:05:47

标签: multithreading http web server

作为一个学校项目,我被要求构建一个简单的HTTP服务器,它可以处理和处理多个同步请求,并证明该服务器将我的主页传送到互联网浏览器。 (使用RFC 1945 / HTTP 1.0)

我参与了我的项目的一部分,所以你知道它是怎么回事   - 服务器侦听固定端口。当它收到TCP连接请求时,它通过另一个端口建立TCP连接,并在单独的线程中为请求提供服务。为简化此编程任务,我们将分两个阶段开发代码。在第一阶段,您将编写一个多线程服务器,它只显示它收到的HTTP请求消息的内容。在此程序正常运行后,您将添加生成适当响应所需的代码。

任何想法或样品?

1 个答案:

答案 0 :(得分:1)

这是一年前我写过的带有线程池的http服务器示例:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <malloc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <signal.h>

#define KB 1024
/*function declerations*/

void send_content(int fd, int connfd,int isDir,char* path);
void enqueue(int data);
void* dequeue(void* ptr);
void send_to_client(int connfd, char* line);

/*global vars*/
pthread_mutex_t lock;
pthread_cond_t emp;
int sock =0;
int max_threads =0;
int global_count=0;
int finished =1;
int length=0;

/*Queue*/
struct node{
    int info;
    struct node *ptr;
}*head,*tail;
struct sigaction ready, last;



int main(int argc, char** argv){

    int threads;
    int requests; 
    int port;
    int i; 
    int s; 
    int len_of_packet=1; 
    int fd;
    int port1; 
    int file;   
    char content[16000];
    char  *fileContent ;
    int thrd ;
    int rc;
    char method[KB];
    char path[KB];
    int s_len=0; 
    int r_len=0;
    int fileLen; 
    int parsing;
    unsigned int addr_size;
    struct sockaddr_in serv_addr, client_addr;
    struct stat st;
    struct dirent *entry;
    DIR *dir;
    static fd_set readsocks, socks;
    char buf[KB] ;
    char Uniform_Resource_Identifier[KB];
    int servfd; 
    int socket_of_accept; 

    off_t chunk=0;
    size_t readData;
    int sent;

    assert (argc ==3 || argc ==4);


    threads = strtol(argv[1], NULL, 10);
    if ((errno == ERANGE && (threads == LONG_MAX || threads == LONG_MIN))
            || (errno != 0 && threads == 0)) {
        perror("Error: strtol has been failed\n");
        exit(0);
    }
    max_threads = threads;
    requests = strtol(argv[2], NULL, 10);
    if ((errno == ERANGE && (requests == LONG_MAX || requests == LONG_MIN))
            || (errno != 0 && requests == 0)) {
        perror("Error: strtol has been failed\n");
        exit(0);
    }
    port = 80; 

    if (argc == 4){
        port = strtol(argv[3], NULL, 10);
        if ((errno == ERANGE && (port == LONG_MAX || port == LONG_MIN))
                || (errno != 0 && port == 0)) {
            perror("Error: strtol has been failed\n");
            exit(0);
        }
    }

    pthread_t threadsArr[threads];


    if(pthread_mutex_init(&lock,NULL)!=0){
        printf("Error: failed to init mutex\n");
        return 1;   
    }

    pthread_cond_init( &emp, NULL);


    head=NULL;
    tail=NULL;

    /*create threads*/
    for (thrd=0; thrd<threads;thrd++){
        rc = pthread_create(&threadsArr[thrd], NULL,  dequeue, (void*) &thrd);
        if (rc){
            printf("Error: failed to create thread\n");
            return 1;   
        }
    }

    /* create socket */
    servfd = socket(AF_INET, SOCK_STREAM, 0);
    sock = servfd;
    if (servfd == -1) {
        printf("Error creating socket\n");
        exit(1);
    }

    /* init prepernces */
    memset(&serv_addr, '0', sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(port);

    /* bind */
    setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, (const char *) &len_of_packet,sizeof(int));
    if (bind(servfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1) {
        printf("Error: Bind has been failed\n");
        perror ("Bind: ");
        exit(1);
    }

    /*listen */
    if (listen(servfd, requests) == -1) {
        printf("Error: Listen has been failed\n");
        perror ("Listen: ");
        exit(1); 
    }

    FD_ZERO(&socks);
    FD_SET(servfd, &socks);

    int startLen = strlen("HTTP/1.0 200 OK\r\n"
            "Content-Type: text/html; charset=UTF-8\r\n\r\n"
            "<doctype !html><html><head><title></title>"
            "<style>body"
            "h1 { font-size:4cm; text-align: center; color: black;"
            "}</style></head>"
            "<body><h1>Service Unavailable (HTTP 503)</h1></body></html>\r\n");
    int endLen = strlen("</h1></body></html>\r\n");


    while(1){

        sigfillset(&ready.sa_mask);
        ready.sa_handler = SIG_IGN;
        ready.sa_flags = 0;

        if (sigaction(SIGINT, &ready,&last) || sigaction(SIGINT,&last,NULL)){
            finished=0;
            for(i=0; i<threads; i++){
                rc = pthread_join(threadsArr[i], NULL);
                if(rc != 0){
                    printf("Error: join has been failed\n");
                    perror("Join: ");
                    return 1;   
                }
            }
            pthread_mutex_destroy(&lock);
            close(sock);
        }


        addr_size = sizeof(client_addr);
        socket_of_accept = accept(servfd, (struct sockaddr *) &client_addr, &addr_size);
        if (socket_of_accept == -1) {
            printf("Error: Accept has been failed\n");
            perror ("Accept: ");
            exit(1);
        }


        enqueue(socket_of_accept);
        if (length <= requests){

            /*recieve message*/
            r_len = recv(socket_of_accept, buf, KB,0);
            if (r_len == -1){
                printf("Error: recieve has been  %d\n", socket_of_accept);
                close(socket_of_accept);
                exit(1);
            }

            sscanf(buf,"%s %s", method, Uniform_Resource_Identifier);
            sscanf(Uniform_Resource_Identifier,"%s", path);
            printf("method = %s, path = %s\n", method, path);

            /*GET OR POST*/
            if(strcmp(method, "GET")!= 0 && strcmp(method, "POST") != 0){
                char response[] = "HTTP/1.0 200 OK\r\n"
                        "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                        "<doctype !html><html><head><title></title>"
                        "<style>body"
                        "h1 { font-size:4cm; text-align: center; color: black;"
                        "}</style></head>"
                        "<body><h1>Service Unavailable (HTTP 503)</h1></body></html>\r\n";
                s_len = send(socket_of_accept, response, strlen(response), 0);
                if (s_len == -1){
                    printf("Error: failed to send message %s to client %d\n", response, socket_of_accept);
                    close(socket_of_accept);
                    exit(1);
                }
            }

            if (stat(path, &st) == -1){ /*file not found*/
                char response[] = "HTTP/1.0 200 OK\r\n"
                        "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                        "<doctype !html><html><head><title></title>"
                        "<style>body"
                        "h1 { font-size:4cm; text-align: center; color: black;"
                        "}</style></head>"
                        "<body><h1>Not Found  (HTTP 404)</h1></body></html>\r\n";
                s_len = send(socket_of_accept, response, strlen(response), 0);
                if (s_len == -1){
                    printf("Error: failed to send message %s to client %d\n", response, socket_of_accept);
                    close(socket_of_accept);
                    exit(1);
                }
                close(socket_of_accept);
            }else {

                if(S_ISDIR(st.st_mode)){/*directory*/

                    if((dir = opendir (path)) != NULL){


                        send_content(fd, socket_of_accept,1,path);

                        close(socket_of_accept);

                    }else{
                        printf("Error: failed to open directory: %s\n", path);
                    }


                }else if(S_ISREG(st.st_mode) && st.st_size != 0){/*regular file*/


                    fd=open(path,O_RDONLY);
                    send_content(fd, socket_of_accept,0,path);

                    close(file);
                    close(socket_of_accept);


                }

                else{/*empty file*/
                    char response[] = "HTTP/1.0 200 OK\r\n"
                            "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                            "<doctype !html><html><head><title></title>"
                            "<style>body"
                            "h1 { font-size:4cm; text-align: center; color: black;"
                            "}</style></head>"
                            "<body><h1> </h1></body></html>\r\n";
                    s_len = send(socket_of_accept, response, strlen(response), 0);
                    if (s_len == -1){
                        printf("Error: failed to send message %s to client %d\n", response, socket_of_accept);
                        close(socket_of_accept);
                        exit(1);

                    }
                }
            }

        }else{/*full queue*/
            /*send (HTTP 503)*/

            char response[3000] = "HTTP/1.0 200 OK\r\n"
                    "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                    "<doctype !html><html><head><title></title>"
                    "<style>body"
                    "h1 { font-size:4cm; text-align: center; color: black;"
                    "}</style></head>"
                    "<body><h1>Service Unavailable  (HTTP 503)</h1></body></html>\r\n";
            s_len = send(socket_of_accept, response, strlen(response), 0);
            if (s_len == -1){
                printf("Error: unable to send a message %s to: %d\n", response, socket_of_accept);
                close(socket_of_accept);
                exit(1);

            }
            close(socket_of_accept);
        }


    }

    return 0;
}






void send_content(int fd, int connfd,int isDir,char* path){
    char line[KB];
    memset(line, 0, sizeof(line));

    send_to_client(connfd, "HTTP/1.0 200 OK\r\n\r\n");

    int return_value;
    if(!isDir){
        while ((return_value=read(fd, line, KB)) > 0) {
            send_to_client(connfd, line);
            memset(line, 0, sizeof(line));
        }
        if (return_value < 0){
            perror("Error read");
            send_to_client(connfd, "Error Resorces\n");
            return;
        }
    }
    else {
        DIR           *d;
        struct dirent *dir;
        d = opendir(path);
        if (d)
        {
            while ((dir = readdir(d)) != NULL)
            {
                strcpy(line,dir->d_name);
                strcat(line,"\n");
                send_to_client(connfd,line);
                memset(line, 0, sizeof(line));
            }

            closedir(d);
        }
    }
}


/*enqueue*/
void enqueue(int data)
{
    int rc;
    struct node* temp;

    temp = (struct node *)malloc(1*sizeof(struct node));
    if(temp == NULL){
        printf("Error: couldn't allocate memory\n");
        exit(1);
    }
    temp->ptr = NULL;
    temp->info = data;

    if (pthread_mutex_lock(&lock) !=0){
        printf("Error: failed to lock\n");
        exit(1);
    } 



    if (length == 0) /*empty queue*/
    {
        head = temp;

    }else 
    {
        tail->ptr = temp; 
    }

    tail = temp;
    length++;
    rc = pthread_cond_broadcast(&emp);
    assert(rc==0);
    if (pthread_mutex_unlock(&lock)!=0){
        printf("Error: failed to unlock\n");
        pthread_exit(0);
    }


    return;
}





void send_to_client(int connfd, char* line){
    int nsent, totalsent;
    int notwritten = strlen(line);

    /* keep looping until nothing left to write*/
    totalsent = 0;
    while (notwritten > 0){
        /* notwritten = how much we have left to write
            totalsent  = how much we've written so far
            nsent = how much we've written in last write() call */
        nsent = write(connfd, line + totalsent, notwritten);
        assert(nsent>=0); // check if error occured (client closed connection?)

        totalsent  += nsent;
        notwritten -= nsent;
    }
}







/*dequeue*/
void* dequeue(void* ptr)
{
    int rc;
    int i=0;
    int thread = *((int*)ptr);

    struct node* temp;

    while(finished){

        if (pthread_mutex_lock(&lock) !=0){
            printf("Error: lock has been failed\n");
            pthread_exit(0);
        }

        while (length == 0)
        {
            rc = pthread_cond_wait(&emp, &lock);
            assert(rc == 0);
        }

        int info = head->info;
        temp = head;
        head = head->ptr;
        free(temp);


        length--;
        rc = pthread_cond_broadcast(&emp);
        assert(rc==0);
        if (pthread_mutex_unlock(&lock)!=0){
            printf("Error: unlock has been failed\n");
            pthread_exit(0);
        }
    }
    pthread_exit(0);
}