代码之家  ›  专栏  ›  技术社区  ›  Jacob

有人能帮我理解为什么我的C Web服务器正在抛出SIGABRT吗

  •  0
  • Jacob  · 技术社区  · 1 年前

    我是C语言的新手,试图构建一个非常简单的Web服务器作为启动项目,但由于某种原因,在处理完一个请求(并正确返回HTML文件)后,我在handleClient函数中得到了SIGABRT

    当我介绍Posix线程时,它就出现了,我认为这与多线程和错误的内存处理有关。有人能给我指明正确的方向吗?或者帮助我找出代码的哪一部分可能是错误的?

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <pthread.h>
    
    void serveFile(int socket, const char* filename) {
        char buffer[1024];
        int fd;
        int readBytes;
    
        fd = open(filename, O_RDONLY);
    
        if (fd == -1) {
            perror("File open failed");
            return;
        }
    
        while ((readBytes = read(fd, buffer, sizeof(buffer))) > 0) {
            write(socket, buffer, readBytes);
        }
    
        close(fd);
    }
    
    void *handleClient(void *arg) {
        int socket = *(int*)arg;
        char buffer[1024];
        int readBytes;
    
        readBytes = read(socket, buffer, sizeof(buffer));
    
        buffer[readBytes] = '\0';
        printf("Received: %s\n", buffer);
    
        char *header = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
        write(socket, header, strlen(header));
    
        serveFile(socket, "./helloworld.html");
    
        close(socket);
    
        free(arg);
    
        return NULL;
    }
    
    int main() {
        int server_fd, new_socket;
        struct sockaddr_in address;
        int opt = 1;
        int addrlen = sizeof(address);
    
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
            perror("socket failed");
            exit(EXIT_FAILURE);
        }
    
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons(8080);
    
        if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
            perror("bind failed");
            exit(EXIT_FAILURE);
        }
    
        if (listen(server_fd, 3) < 0) {
            perror("listen");
            exit(EXIT_FAILURE);
        }
    
        while(1) {
            printf("Waiting for connections...\n");
    
            int *client_fd = malloc(sizeof(int));
    
            if ((*client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
                perror("accept");
                continue;
            }
            
            pthread_t thread_id;
    
            if(pthread_create(&thread_id, NULL, handleClient, (void *)client_fd)) {
                perror("Could not create thread");
                continue;
            }
    
            pthread_detach(thread_id);        
        }
    
        return 0;
    }
    

    edit:这是服务器从客户端接收的内容:

    Received: GET / HTTP/1.1
    Host: localhost:8080
    Connection: keep-alive
    Cache-Control: max-age=0
    sec-ch-ua: "Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
    sec-ch-ua-mobile: ?0
    sec-ch-ua-platform: "macOS"
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    Sec-Fetch-Dest: document
    Accept-Encoding: gzip, deflate, br, zstd
    Accept-Language: en-US,en;q=0.9,sv-SE;q=0.8,sv;q=0.7
    Cookie: access_token="token"
    

    我使用m3芯片在macOS上运行和编译。我正在使用最新版本的Chrome进行测试,这就是错误消息在终端中的样子

    zsh: abort      ./http_server
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Andrew Henle    1 年前

    您的代码中有几个问题对我来说很明显。

    首先,这是一个潜在的缓冲区溢出:

    char buffer[1024];
    int readBytes;
    
    readBytes = read(socket, buffer, sizeof(buffer));
    
    buffer[readBytes] = '\0';
    

    如果你读了整整1024个字节,就会溢出 buffer .

    如果你想将数据读取为以nul结尾的字符串,你必须考虑必须自己添加它:

    char buffer[1024];
    int readBytes;
    
    readBytes = read(socket, buffer, sizeof(buffer) - 1);
    
    buffer[readBytes] = '\0';
    

    此铸造到 socklen_t 可能完全错误:

        if ((*client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
            ...
    

    如果 addrlen 不是 socklen_t 这个 accept() 可以覆盖内存,如果 socklen_t 大于 int addrlen . 从不 扔掉警告。

    可能还有其他问题——我很快就注意到了这些问题。