基于fork的C语言登录服务器多次连接无响应问题排查
问题分析与修复方案
核心问题
- 共享文件描述符冲突:父进程打开的文件描述符被所有子进程共享,子进程通过
fdopen创建的FILE指针会互相干扰文件偏移量,导致读取位置错误,无法正确处理请求。 - 未处理网络调用错误:
recv/send未检查返回值,数据传输失败时直接进入后续逻辑,导致无响应。 - 父进程过早关闭客户端Socket:fork后父进程立刻关闭Socket,可能导致子进程Socket被意外销毁。
- 无文件锁机制:多进程同时读写TXT文件,出现竞态条件,读取到不完整数据。
- 字符串长度计算错误:服务器中
send的字符串与strlen参数大小写不一致,导致发送内容不完整。 - 未初始化
addr_size:accept调用时addr_size未赋值,可能引发未定义行为。
修复步骤
1. 每个子进程独立打开文件
移除全局FILE* fp,子进程内单独打开数据库文件,避免共享文件偏移:
if ((childpid = fork()) == 0){ // ... 初始化代码 FILE *local_fp = fopen("baza.txt", "a+"); if (!local_fp) { perror("无法打开数据库文件"); send(client_socket, "服务器内部错误", strlen("服务器内部错误"), 0); close(client_socket); exit(EXIT_FAILURE); } // ... 后续逻辑 }
2. 检查网络调用返回值
每次recv/send都要验证结果,处理错误:
// 示例:接收决策指令 ssize_t recv_len = recv(client_socket, decision, sizeof(decision)-1, 0); if (recv_len == -1) { perror("接收数据失败"); close(client_socket); fclose(local_fp); exit(EXIT_FAILURE); } decision[recv_len] = '\0'; // 确保字符串以'\0'结尾
3. 处理僵尸进程并延迟Socket关闭
添加SIGCHLD信号处理,避免僵尸进程,父进程无需立刻关闭客户端Socket:
void sigchld_handler(int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); } int main() { signal(SIGCHLD, sigchld_handler); // ... 初始化代码 }
4. 添加文件锁
使用flock确保同一时间只有一个进程读写文件:
#include <sys/file.h> // 打开文件后加排他锁 if (flock(fileno(local_fp), LOCK_EX) == -1) { perror("文件加锁失败"); fclose(local_fp); close(client_socket); exit(EXIT_FAILURE); } // ... 读写操作完成后解锁 flock(fileno(local_fp), LOCK_UN);
5. 修复字符串长度bug
修正send中的字符串长度参数,确保大小写一致:
send(client_socket,"Znaleziono login",strlen("Znaleziono login"),0);
6. 初始化addr_size
accept前赋值addr_size:
socklen_t addr_size = sizeof(client_address); int client_socket = accept(server_socket, (struct sockaddr*)&client_address, &addr_size);
内容的提问来源于stack exchange,提问作者Karol Hynek
相关产品推荐
相关产品推荐

