C语言多线程Socket调用accept()时出现Bad file descriptor错误
自定义HTTP服务器accept()报错:bad file descriptor
需求
重写网站上使用的旧版遗留自定义HTTP服务器。
问题现象
调用accept()函数时,终端输出Can't accept client: bad file descriptor错误,代码表面无明显问题。
已尝试的解决方法
- 调整代码执行顺序
- 怀疑自有模板存在隐藏错误,曾复制网上随机模板测试,但因不满意换回自己的代码
相关代码
#include <stdio.h> #include <netinet/in.h> #include <sys/socket.h> #include <unistd.h> #include <pthread.h> #include <signal.h> #include <stdlib.h> #include <string.h> // This version of software is licensed under public domain feel free to use for any purpose typedef struct{ char* http_path; void (*function)(); } location; unsigned short server_count = 0; typedef struct{ unsigned char running; unsigned id; unsigned locationCount; location* locations; unsigned port; } http_server_t; http_server_t* servers; char nServer(unsigned short port) { server_count++; char status = server_count; // The warning happens here can be disregarded if(server_count == 1) { status = (servers = malloc(sizeof(http_server_t)*server_count)) != NULL; }else{ status = (servers = realloc(servers, sizeof(http_server_t)*server_count)) != NULL; } if(!status){ return -1; } servers[server_count-1].port = port; servers[server_count-1].running = 1; servers[server_count-1].locationCount = 0; servers[server_count-1].locations = NULL; return server_count; } typedef struct __attribute__((packed)) { unsigned sock; struct sockaddr_in inf; } serverHandlerData; void serveClient() { } void* socketHandle(void* data) { serverHandlerData *fixed = data; unsigned sock = fixed->sock; struct sockaddr_in in = fixed->inf; free(data); // 释放动态分配的参数内存 if(bind(sock, (struct sockaddr *)&in, sizeof(struct sockaddr))) { perror("Can't bind"); close(sock); return NULL; } if((listen(sock, 5)) < 0) { perror("Can't listen the socket"); close(sock); return NULL; } for(;;) { int csock; struct sockaddr_in caddr; socklen_t caddr_len = sizeof(caddr); if((csock = accept(sock, (struct sockaddr*)&caddr, &caddr_len)) < 0) { perror("Can't accept client"); close(sock); return NULL; } unsigned char* buffer = malloc(101); // 多留一个字节存终止符 if(buffer) { ssize_t read_len = read(csock, buffer, 100); if(read_len > 0) { buffer[read_len] = '\0'; // 添加字符串终止符 printf("%s", buffer); } free(buffer); } close(csock); } return NULL; } void iServers() { for(unsigned long x = 0; x < server_count; x++) { struct sockaddr_in addr; unsigned int sock = 0; if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket can't be made due a error"); return; } printf("Starting server on port %d\n", servers[x].port); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(servers[x].port); // 动态分配参数,避免局部变量销毁问题 serverHandlerData* s = malloc(sizeof(serverHandlerData)); if(!s) { perror("Failed to allocate server data"); close(sock); return; } s->sock = sock; s->inf = addr; pthread_t serverHandle; if(pthread_create(&serverHandle, NULL, socketHandle, s) != 0) { perror("Failed to create thread"); free(s); close(sock); return; } pthread_detach(serverHandle); // 分离线程,避免资源泄漏 } } unsigned char running = 1; void ctrlCHandler(int sig){ running = 0; } int main() { signal(SIGINT, ctrlCHandler); if(nServer(4080) == -1) { perror("Failed to create server 1"); return 1; } if(nServer(1010) == -1) { perror("Failed to create server 2"); return 1; } iServers(); // 保持主线程运行,等待信号 while(running) { sleep(1); } // 清理服务器资源 for(unsigned long x = 0; x < server_count; x++) { free(servers[x].locations); } free(servers); return 0; }
问题根源与修复说明
- accept()参数错误:原代码中
accept(csock, ...)使用了未初始化的csock作为监听套接字参数,正确应该传入之前创建的sock(即fixed->sock),同时第三个参数需要传入socklen_t类型的指针,而非直接传值。 - 内存分配与数组索引错误:
nServer中malloc(sizeof(servers)*server_count)错误使用指针大小计算内存,应改为sizeof(http_server_t);数组索引从0开始,servers[server_count]越界,需改为servers[server_count-1]。 - 线程传参非法:原代码传递局部变量
s的地址给线程,线程启动时局部变量已销毁,改为动态分配serverHandlerData内存,在线程内释放。 - 服务器遍历循环错误:原循环
x=1; x<server_count会漏掉第一个服务器,改为x=0; x<server_count遍历所有服务器。 - listen()参数不合法:
listen(sock, 0)的backlog参数不符合POSIX标准,改为合理值5。 - 不安全的printf调用:原
printf(buffer)可能因缓冲区无终止符或包含格式化字符导致错误,改为printf("%s", buffer)并确保缓冲区以'\0'结尾。 - 资源泄漏问题:添加线程分离
pthread_detach,主线程等待信号后清理服务器资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者drk1
相关产品推荐
相关产品推荐

