0

我目前正在完成我的计算机科学学士学位的最后一年作业,其中之一是使用 POSIX 线程进行并发。分配的是一个非常简单的 http 服务器,它在单独的线程中处理 http 请求并返回简单的 html 文件。在线程函数结束时释放和取消字符缓冲区时,我遇到了一些信号中止错误。我在 X-Code 中工作,当我在方案中启用 Guard Malloc 时,它工作得很好。此外,如果我删除对 free() 的调用并且取消该程序也可以正常运行。我假设后者正在消耗先前线程中使用的内存,而不是为其他进程释放它,因此不是一个好的解决方案,对吗?我不了解 Guard Malloc,但我将提交一个原始的 .cpp 文件以在 Lecturer 上进行编译

下面是完整的代码。任何你愿意扔给我的评论、风格批评或任何类型的智慧珍珠都会受到深深的赞赏。

崩溃的输入是来自这个随机网站的 .html 主页 - http://www.budgie-info.com/

提前谢谢了。

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

// Define the port number to identify this process
#define MYPORT 3490

typedef struct socket_fd {
    unsigned fd;
} sock_fd;

void *request(void *fd);

int main() {
    int s;
    //unsigned fd;
    struct sockaddr_in my_addr;
    pthread_t t;
    int retval;

    sock_fd *s_fd;

    // Construct address information
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    memset(my_addr.sin_zero, '\0', sizeof(my_addr.sin_zero) );

    // Create a socket and bind it the port MYPORT
    s=socket(PF_INET,SOCK_STREAM, 0);
    int yes = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

    retval = bind(s, (struct sockaddr *)&my_addr, sizeof(my_addr));
    if( retval != 0) {
        fputs ("bind() failed.",stderr);
        exit (7);
    }

    printf("%d\n", my_addr.sin_port);
    retval = listen(s,10);
    if( retval != 0 ) {   // Allow up to 10 incoming connections
        fputs ("listen() failed.",stderr);
        exit (9);
    }

    while(1) {
        s_fd = (sock_fd*)malloc(sizeof(sock_fd));
        s_fd->fd=accept(s,NULL,NULL);             // wait for a request
        retval = pthread_create( &t, NULL, request, s_fd);
        free(s_fd);
        s_fd = NULL;
        if(retval != 0) {
            printf("pthread_create() failed. error %d\n", retval);
        }
    }
}

void *request(void *s_fd) {
    int retval;
    char data[65536];
    char header[]="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
    char header_gif[]="HTTP/1.1 200 OK\r\nContent-Type: image/gif\r\n\r\n";
    char header_jpeg[]="HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\n\r\n";
    char header_png[]="HTTP/1.1 200 OK\r\nContent-Type: image/png\r\n\r\n";
    int header_len;
    char *send_buffer;
    char *temp_buffer;
    char filename[256];
    char filename_parsed[256];
    FILE *f;

    sock_fd *fd_t = (sock_fd*)s_fd;
    unsigned fd = fd_t->fd;

    int cnt = 0;
    do {
        retval=(int)recv(fd,data,65536,0);              // recieve the request using fd
        ++cnt;
    } while(retval == 65536);

    if(retval < 0) {
        fputs ("recv() failed.\n",stderr);
        return NULL;
    }

    data[retval]='\0';                                  // NUL terminate it

    retval = sscanf((char *)data,"GET /%s ",filename);   // get the name of the file
    if(retval != 1) {
        fputs ("sscanf() failed.\n",stderr);
        return NULL;
    }
    if(strlen(filename) > 256) {
        fputs ("Filename overflow.\n",stderr);
        return NULL;
    }

    // parse uml spaces out of filenames
    int j = 0;
    for(int i = 0; filename[i]!='\0'; ++i) {
        if(filename[i] == '%') {
            filename_parsed[j] = ' ';
            i+=2;
        } else {
            filename_parsed[j] = filename[i];
        }
        ++j;
    }
    filename_parsed[j] = '\0';

    //print received header
    printf("Receiving:\n %s\n", data);
    //print requested filename
    printf("\n\n-------------filename = %s|\n\n", filename_parsed);

    if( (f=fopen(filename_parsed,"rb")) == NULL ) { // open the file (might be binary)
        fputs ("fopen() failed.\n",stderr);
        return NULL;
    }

    // obtain file size:
    size_t lSize;
    fseek (f , 0 , SEEK_END);
    lSize = ftell (f);
    rewind (f);

    // pre calculate length of filename
    int len = (int)strlen(filename_parsed);

    // identify appropriate header, alocate required memory and copy the header into the buffer
    if(0 == strcmp(filename_parsed + len - 4, ".gif")) {
        header_len = (int)strlen(header_gif);
        printf("\n\n\n\n\nG I F\n\n\n\n");
        send_buffer = (char*) malloc ( sizeof(char)*((lSize+(size_t)header_len)+14) ); // WARNING: hardcoded margin for header size differences
        memcpy(send_buffer, header_gif, header_len);
    } else if(0 == strcmp(filename_parsed + len - 5, ".jpeg")) {
        printf("\n\n\n\n\nJ P E G\n\n\n\n");
        header_len = (int)strlen(header_jpeg);
        send_buffer = (char*) malloc ( sizeof(char)*((lSize+(size_t)header_len)+14) ); // WARNING: hardcoded margin for header size differences
        memcpy(send_buffer, header_jpeg, header_len);
    } else if(0 == strcmp(filename_parsed + len - 4, ".png")) {
        header_len = (int)strlen(header_png);
        printf("\n\n\n\n\nP N G \n\n\n\n");
        send_buffer = (char*) malloc ( sizeof(char)*((lSize+(size_t)header_len)+14) ); // WARNING: hardcoded margin for header size differences
        memcpy(send_buffer, header_png, header_len);
    } else {
        header_len = (int)strlen(header);
        send_buffer = (char*) malloc ( sizeof(char)*((lSize+(size_t)header_len)+14) ); // WARNING: hardcoded margin for header size differences
        memcpy(send_buffer, header, header_len);
    }

    // allocate memory to contain the whole file:
    temp_buffer = (char*) malloc (sizeof(char)*(lSize+10));   // WARNING: hardcoded margin for header size differences
    if (temp_buffer == NULL) {
        fputs ("malloc() failed.\n",stderr);
        return NULL;
    }

    // copy the file into the buffer:
    retval = (int)fread (temp_buffer,1,lSize,f);
    if (retval != lSize) {
        fputs ("fread() failed.\n",stderr);
        return NULL;
    }

    memcpy(send_buffer + header_len, temp_buffer, retval);

    //print packet being sent
    printf("Sending:\n%s\n", send_buffer);

    memcpy(send_buffer + retval, "\r\n", 4);

    // send packet
    retval = (int)send(fd,send_buffer,retval,0);
    if(retval < 0) {
        fputs ("send() failed.\n",stderr);
        return NULL;
    }

    free(temp_buffer);    // The section of memory management causes SIGABRT errors everytime i run, commenting them out made it run smoothly.
    temp_buffer = NULL;   // Is the closing of this function tidying up for me or is it leaving a memory leak trail?
    free(send_buffer);    // Is it the multi threading that is making memory management buggy?
    send_buffer = NULL;
    fclose(f);
    f = NULL;

    close(fd);  // close the socket
    return NULL;
}
4

2 回答 2

2

这段代码有很多问题。考虑到潜在问题的数量,很难说为什么它会在这些文件上崩溃(您没有提供导致程序崩溃的示例输入)

  • 要使这个问题可用,您需要在套接字上设置 SO_REUSEADDR,否则如果它崩溃(提示,提示),您将无法立即重新启动它并接收连接

  • 您不检查 bind()、accept()、pthread_create() 的返回值

  • 你只需要调用一次listen(),而不是在循环中

  • 将 fd 转换为 long before 将其转换为 void * 会更安全,因为这就是您在聆听时所做的。一种更简洁的方法是在堆上建立一个结构,其中包含一个 fd

  • 不要定义你在编译时设置的数组的大小,让编译器来做。更安全:char header[]="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";. 那么你就不需要调用 strlen 了。你可以只使用sizeof(header) - 1.

  • 您不检查 recv() 的返回值。您必须确保它没有失败(尤其是因为您将它用作数组中的索引)

  • 您不能确保文件名没有溢出。您读取了高达 65K 的内容,但您不确定文件名是否足够大(即,使程序崩溃很容易,而且这是一个安全漏洞)

  • 您没有读取 sscanf 的返回值。不能保证文件名有数据

  • 你不是 nul 终止 filename_parsed (我相信这是你的程序崩溃的主要原因)

  • 您可能只调用一次 strlen(filename_parsed) 而不是调用它无数次

  • 为什么不使用 strcmp (当然,一旦 filename_parsed 正确终止),而不是在最后进行令人困惑的字符检查:0 == strcmp(filename_parsed + len - 5, ".jpeg")等等。

  • 检查发送的返回值
于 2013-07-30T04:27:53.673 回答
1

您可以检查一件事:从 fread() 读取的内容不一定是字符串 - 可能根本不会以 NULL 结尾。因此,在此使用 strcat 不是正确的方法。考虑将其更改为 memcpy() 或其他内容。

于 2013-07-31T06:56:36.270 回答