网络服务器有时无法发送所有图像

webserver sometimes fails to send all images

所以我已经在 Web 服务器上工作了几个月,但我无法解决这个问题。这个问题是大多数时候,当服务器尝试将数据发送到 Web 客户端时,一些图像不会发送 across/don 不会加载。我已经研究了一段时间,但没有找到解决方案。我知道它有很多代码,但这是让它正常工作的最后一件事。

代码如下:

//this is where you enter the default file directories
char *defaultDir1 = "/enter/website/path/here";
char *defaultDir2 = "/enter/server/path/here";




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



struct sockaddr_in sockaddr;
char webFileDir[9000];
char servFileDir[9000];
#define THREAD_POOL_SIZE 200
#define BACKLOG 200

pthread_t thread_pool[THREAD_POOL_SIZE];




//creates a lock and unlock system for enqueue and dequeue
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//creates a sleep timer for the threads/does something until given a signal or condition.
pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;

struct node {
    int *client_socket;
    struct node *next;
};
typedef struct node node_t;

node_t *head = NULL;
node_t *tail = NULL;

void enqueue(int *client_socket) {
    node_t *newnode = malloc(sizeof(node_t)); //create node
    newnode->client_socket = client_socket; //assign client socket
    newnode->next = NULL; //add node to the end of the list
    if (tail == NULL) { //checks if there is nothing on the tail and if so adds a head
        head = newnode;
    } else {
        //if there is a tail then add a new node onto the tail
        tail->next = newnode;
    }
    tail = newnode;
    //this all basically creates a new node then moves it over over and over again until stopped
}

int *dequeue() {
    if (head == NULL) {
        //if the head is empty or nothing has been enqueued then return null
         return NULL;
    }
    if (head != NULL) { //else...
        int *result = head->client_socket;
        node_t *temp = head;
        head = head->next;
        if (head == NULL) {tail = NULL;}
        free(temp);
        return result;
    }
    //this function removes everything off of the queue
    return 0;
}



int *handleClient(int *pClientSock) {
    FILE *fpointer;
    char *findExtention;
    char *clientIP;
    char *endDir;
    int freadErr;
    int fsize;
    int bin;
    int readSock;
    char recvLine[2];
    char fileDir[9000];
    char recvLineGET1[70];
    char recvLineGET[60];
    char httpResponseReport[1000];
    char *fileLine;
    char httpResponse[1000];
    char *endOfPost;
    char *splitPost;
    char *postContentLength;
    char *finalPostLength;
    char tempPost[2000];
    int contentLen;
    char *refindBody;
    char finalBody[100000];
    char fullResp[100000];
    char *sendFileDirectory;
    int checkSend;
    char *startDir;
    char *getEnd;
    int responseCode = 200;
    
    
    memset(fileDir, 0, sizeof(fileDir));
    memset(recvLineGET, 0, sizeof(recvLineGET));


    //get client ip
    int acceptSock = *pClientSock;
    socklen_t addrSize = sizeof(struct sockaddr_in);
    getpeername(acceptSock, (struct sockaddr *)&sockaddr, &addrSize);
    clientIP = inet_ntoa(sockaddr.sin_addr);



    fullResp[0] = '[=10=]';
    //handles reading client http request
    while (1) {
        if ((readSock = read(acceptSock, recvLine, 1)) != 1) {
            perror("error reading from socket");

            send(acceptSock, "HTTP/1.1 500\r\n\r\n", 16, MSG_NOSIGNAL);

            memset(recvLine, 0, sizeof(recvLine));
            memset(fullResp, 0, sizeof(fullResp));

            close(acceptSock);
            return 0;
        }
        recvLine[1] = '[=10=]';
        strcat(fullResp, recvLine);
        printf("%s", recvLine);

        if ((endOfPost = strstr(fullResp, "\n")) != NULL) {
            break;
        } else if (readSock < 0) {
            memset(fullResp, 0, sizeof(fullResp));

            close(acceptSock);
            return 0;
        }
        memset(recvLine, 0, sizeof(recvLine));
    }
    printf("fullResp: %s\n", fullResp);





    strcpy(fileDir, webFileDir);


    //
    //POSSIBLE ERROR HERE
    //
    if ((getEnd = strstr(fullResp, "HTTP")) == NULL) {
        memset(fullResp, 0, sizeof(fullResp));

        send(acceptSock, "HTTP/1.0 400\r\n\r\n", 16, MSG_NOSIGNAL);

        close(acceptSock);
        return 0;
    }

    strcpy(getEnd, "");

    if ((startDir = strchr(fullResp, '/')) == NULL) {
        perror("this shouldnt happen .-.");
        printf("startDir: %s\n", startDir);

        send(acceptSock, "HTTP/1.0 400\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fullResp, 0, sizeof(fullResp));

        close(acceptSock);
        return 0;
    }



    //handles the file retrieving




    if ((endDir = strchr(startDir, ' ')) == NULL) {
        perror("endDir error");

        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fileDir, 0, sizeof(fileDir));
        memset(fullResp, 0, sizeof(fullResp));

        close(acceptSock);
        return 0;
    }


    strcpy(endDir, "");



    
    //checks for requested directory
    if (strcmp(startDir, "/") == 0) {
        findExtention = ".html";
        strcpy(fileDir, webFileDir);
        strcat(fileDir, "index.html");
        responseCode = 200;
    } else {
        if ((findExtention = strchr(startDir, '.')) == NULL) {
            perror("invalid webpage");

            findExtention = ".html";
            strcpy(fileDir, servFileDir);
            strcat(fileDir, "err404.html");
            responseCode = 404;
        }

        strcat(fileDir, startDir);
    }

    //
    //START POSSIBLE ERROR
    //
    if ((fpointer = fopen(fileDir, "rb")) != NULL) {
        fseek(fpointer, 0L, SEEK_END);
        fsize = ftell(fpointer);
    } else if (strcmp(startDir-1, "/favicon.ico") == 0 && access(fileDir, F_OK) != 0) {
        strcpy(fileDir, servFileDir);
        strcat(fileDir, "favicon.ico");

        if (access(fileDir, F_OK) != 0) {
            printf("\n\n\nERROR: please do not delete the default favicon.ico file. the program will not work properly if it is deleted\n\n\n");

            //error 500: internal server error
            send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

            exit(1);
        }
        
        if ((fpointer = fopen(fileDir, "rb")) == NULL) {
            perror("fopen error");

            //error 500: internal server error
            send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

            memset(fullResp, 0, sizeof(fullResp));

            close(acceptSock);
            return 0;
        }

        fseek(fpointer, 0L, SEEK_END);
        fsize = ftell(fpointer);
    } else if (access(fileDir, F_OK) != 0) {
        perror("webpage doesnt exist");
        
        findExtention = ".html";
        strcpy(fileDir, servFileDir);
        strcat(fileDir, "err404.html");
        responseCode = 404;
        if ((fpointer = fopen(fileDir, "r")) == NULL) {
            perror("fopen error");

            send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

            memset(fullResp, 0, sizeof(fullResp));

            close(acceptSock);
            return 0;
        }
        fseek(fpointer, 0L, SEEK_END);
        fsize = ftell(fpointer);
    }
    //
    //END POSSIBLE ERROR
    //


    fclose(fpointer);



    //sets the server http response
    if ((strcmp(findExtention, ".jpeg")) == 0 || (strcmp(findExtention, ".jpg")) == 0) {
        bin = 1;
        sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: image/jpeg\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
    } else if ((strcmp(findExtention, ".png")) == 0) {
        bin = 1;
        sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nnContent-Type: image/png\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
    } else if ((strcmp(findExtention, ".ico")) == 0) {
        bin = 1;
        sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: image/x-icon\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
    } else if ((strcmp(findExtention, ".mp3")) == 0) {
        bin = 1;
        sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: audio/mpeg\r\nContent-length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
    } else if ((strcmp(findExtention, ".html")) == 0) {
        bin = 0;
        sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", responseCode, fsize);
    } else {
        strcpy(fileDir, "err404.html");
        fpointer = fopen(fileDir, "r");
        fseek(fpointer, 0L, SEEK_END);
        fsize = ftell(fpointer);
        fclose(fpointer);
        bin = 0;
        sprintf(httpResponse, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", fsize);
    }

    strcpy(httpResponseReport, httpResponse);



    if (readSock < 0) {
        perror("readsock is less than 0");

        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fullResp, 0, sizeof(fullResp));
        memset(fileDir, 0, sizeof(fileDir));
        memset(httpResponseReport, 0, sizeof(httpResponseReport));
        memset(httpResponse, 0, sizeof(httpResponse));

        close(acceptSock);
        return 0;
    }


    //checks if i need to read plaintext or binary
    if (bin == 0) {
        fpointer = fopen(fileDir, "r");
    } else if (bin == 1) {
        fpointer = fopen(fileDir, "rb");
    }


    if (fpointer == NULL) {
        perror("file open error");

        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fullResp, 0, sizeof(fullResp));
        memset(fileDir, 0, sizeof(fileDir));
        memset(httpResponseReport, 0, sizeof(httpResponseReport));
        memset(httpResponse, 0, sizeof(httpResponse));

        close(acceptSock);
        return 0;
    }

    



    //sends server http response to client
    fseek(fpointer, 0L, SEEK_END);
    fsize = ftell(fpointer);
    fclose(fpointer);




    //checks if i need to read plaintext or binary (again)
    if (bin == 0) {
        fpointer = fopen(fileDir, "r");
    } else if (bin == 1) {
        fpointer = fopen(fileDir, "rb");
    }

    if (fpointer == NULL) {
        perror("fopen error");

        //error 500: internal server error
        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fullResp, 0, sizeof(fullResp));
        memset(fileDir, 0, sizeof(fileDir));
        memset(httpResponse, 0, sizeof(httpResponse));
        memset(httpResponseReport, 0, sizeof(httpResponseReport));

        close(acceptSock);
        return 0;
    }

    fileLine = malloc(fsize * sizeof(char));

    

    while ((freadErr = fread(fileLine, fsize, fsize, fpointer)) > 0);
    
    if (feof(fpointer) < 0) {
        perror("fread error");

        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        memset(fullResp, 0, sizeof(fullResp));
        memset(fileDir, 0, sizeof(fileDir));
        memset(httpResponseReport, 0, sizeof(httpResponseReport));
        memset(httpResponse, 0, sizeof(httpResponse));

        close(acceptSock);
        return 0;
    }


    //set 5 second socket timeout
    struct timeval timeout;
    timeout.tv_sec = 15;
    timeout.tv_usec = 0;

    if (setsockopt(acceptSock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
        perror("setsockopt error");

        //error 500: internal server error
        send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);

        close(acceptSock);
        return 0;
    }


    //
    //POSSIBLE ERROR HERE
    //
    while (1) {
        if ((checkSend = send(acceptSock, httpResponse, 
    strlen(httpResponse), MSG_NOSIGNAL)) == -1) {
            break;
        }


        //send full response
        if ((checkSend = send(acceptSock, fileLine, fsize, 
    MSG_NOSIGNAL)) == -1) {
            break;
        }
        sleep(1);
    }
    //
    //END POSSIBLE ERROR
    //
    

    memset(httpResponse, 0, sizeof(httpResponse));
    memset(fullResp, 0, sizeof(fullResp));


    fclose(fpointer);
    close(acceptSock);
    free(fileLine);



    //handles the clearing of variables and logs client ip and requested directory
    printf("\nclient with the ip: %s has requested %s.\n", clientIP, fileDir);

    memset(fileDir, 0, sizeof(fileDir));
    memset(httpResponseReport, 0, sizeof(httpResponseReport));
    return 0;
}



//assign work to each thread
void *giveThreadWork() {
    while (1) {
        int *pclient;

        pthread_mutex_lock(&mutex);
        //makes thread wait until signaled
        if ((pclient = dequeue()) == NULL) {
            pthread_cond_wait(&condition_var, &mutex);
            pclient = dequeue();
        }
        pthread_mutex_unlock(&mutex);

        //i dont think i need this rn. might need it later so dont delete
        if (pclient != NULL) {
            handleClient(pclient);
        } else {
            //printf("\n\n\n\n\n no more listener \n\n\n\n\n");
        }
    }
}


int main() {
    int clientSock;
    int readSock;
    int sock;
    int portNum;
    int defaultOrNo;
    int openDir;
    char readTemp[1];




    printf("\n\n\nWeb Server\n\n\n");

    printf("would you like to use default directories (1 = yes, 0 = no): ");
    scanf("%d", &defaultOrNo);

    if (defaultOrNo == 0) {
        printf("enter the directory of the files to be served (with '/' at the end): ");
        scanf("%s", webFileDir);

        printf("enter the directory of the web server folder (with '/' at the end): ");
        scanf("%s", servFileDir);
    } else if (defaultOrNo == 1) {
        strcpy(webFileDir, defaultDir1);
        strcpy(servFileDir, defaultDir2);
    }

    printf("what port would you like to host the site on?: ");
    scanf("%d", &portNum);

    sock = socket(AF_INET, SOCK_STREAM, 0);

    if (sock < 0) {
        perror("sock error");
    }



    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    sockaddr.sin_port = htons(portNum);

    if ((bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) < 0) {
        perror("bind error");
        exit(1);
    }

    printf("socket bind success\n");

    //create all the threads for the thread pool
    for (int i = 0; i < THREAD_POOL_SIZE; i++) {
        pthread_create(&thread_pool[i], NULL, giveThreadWork, NULL);
    }

    printf("created thread pool of size %d successfully\n", THREAD_POOLSIZE);

    if ((listen(sock, BACKLOG)) < 0) {
        perror("listen error");
    }

    printf("listen success\n");




    while (1) {
        //checks for client connection
        if ((clientSock = accept(sock, NULL, NULL)) < 0) {
            perror("accept error");
            //oh no too bad
        } else {
            int *pclient = &clientSock;

            pthread_mutex_lock(&mutex);
            enqueue(pclient);
            //sends a signal to wait until needed
            pthread_cond_signal(&condition_var);
            pthread_mutex_unlock(&mutex);
        }
    }

    return 0;
}

最新编辑:我没有收到来自客户的一些请求,或者我没有正确发送它们。我会在找出问题后编辑问题。

我已经标记了我认为主要问题是使用注释的地方,这样你们调试起来会更容易。祝你好运,非常感谢任何能解决这个问题的人!!!

ps:特别感谢@AndreasWenzel 的所有帮助

您的链表应包含必须处理的 HTTP 请求的所有套接字文件描述符的列表。因此,链表中的每个节点都应该包含一个文件描述符。

您目前正在做的是创建一个链表,其中每个节点都包含一个指向文件描述符的指针。但是,每个节点都指向相同的文件描述符变量(指向函数 main 中的 clientSock)。因此,您的链表实际上只存储了一个文件描述符,每当有新的 HTTP 请求到达时,它就会不断被覆盖。

因此,您应该更改链表节点的定义,以便每个节点都包含自己的文件描述符(而不是指向文件描述符的指针):

struct node {
    int client_socket;
    struct node *next;
};

此外,您应该更改函数 enqueuehandleClient 的函数原型以接受 int 参数而不是 int *dequeue 应该 return 一个 int 而不是 int *。当列表为空时,函数 dequeue 应该 return -1。应该使用这个值,因为它不能表示有效的文件描述符。

当然,您还应该相应地调整调用这些函数的代码。