服务器子进程跨客户端发送数据时出现Bad file descriptor错误求助
解决子进程跨客户端发送数据时的"Bad file descriptor"错误
我一眼就看出问题所在了——你踩了fork进程后文件描述符(fd)继承的坑!
先给你拆解下为什么会报这个错:
你每次accept一个客户端就fork一个子进程,fork只会复制当前进程已打开的fd,后续父进程新accept的socket fd,之前的子进程根本就没持有。
比如:
- 第一个客户端连接,accept得到fd=4,存入ids[0]=4,然后fork子进程A。这时候子进程A里只有fd=4是有效的,ids[1]还没值。
- 第二个客户端连接,父进程accept得到fd=5,存入ids[1]=5,然后fork子进程B。这时候子进程B里有fd=5有效,但子进程A里的fd=5完全是个无效的数值——因为子进程A在fork的时候,这个fd还没被创建,它的进程里根本没有打开这个socket,自然调用send就会报"Bad file descriptor"。
你的打印输出4 4 5也能佐证这点:子进程A里的mysocket是4,ids[0]=4(有效),ids[1]=5(但这个fd在子进程A里不存在)。
怎么解决?
给你几个可行的方案,按实现复杂度排序:
方案1:父进程统一负责转发(最推荐)
让子进程只处理自己对应客户端的接收逻辑,要转发的数据交给父进程来发送。父进程持有所有客户端的有效fd,由它来完成跨客户端的消息转发。
大致修改思路:
- 父进程用
select/poll监听所有客户端socket,同时可以和子进程通过管道通信(子进程把要转发的数据和目标客户端信息发给父进程)。 - 子进程只负责接收自己客户端的消息,然后把消息和目标标识(比如要发给另一个客户端)写入管道,父进程读取后转发给对应fd。
简化代码示例(核心逻辑):
// 提前创建父进程和子进程通信的管道 int pipe_fd[2]; pipe(pipe_fd); int *ids = (int *)mmap(NULL,5*sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); int i = 0; while(1) { int mysocket = accept(sock, NULL, NULL); printf("\nClient connected ...\n "); ids[i] = mysocket; i++; printf(" socket and id %d %d", mysocket, ids[i-1]); int pid = fork(); if (pid == 0) { close(pipe_fd[0]); // 子进程关闭读端 char buf[1024]; while(1) { ssize_t n = recv(mysocket, buf, sizeof(buf)-1, 0); if(n <= 0) { close(mysocket); exit(0); } buf[n] = '\0'; // 把要转发的目标fd和数据发给父进程,这里简单拼接,实际可以用结构体 char msg[2048]; snprintf(msg, sizeof(msg), "%d:%s", (i==1 ? ids[1] : ids[0]), buf); write(pipe_fd[1], msg, strlen(msg)+1); if (strcmp(buf, "CLOSE") == 0) { close(mysocket); break; } } exit(0); } else { close(pipe_fd[1]); // 父进程关闭写端 // 父进程用select监听pipe_fd[0]和所有客户端fd,处理转发 char msg[2048]; while(read(pipe_fd[0], msg, sizeof(msg)) > 0) { char *sep = strchr(msg, ':'); if(sep) { int target_fd = atoi(msg); char *data = sep+1; send(target_fd, data, strlen(data), 0); } } } }
方案2:在子进程间传递文件描述符(复杂但直接)
如果一定要让子进程直接访问其他客户端的fd,不能只传递fd数值,要通过UNIX域套接字用sendmsg和recvmsg来传递实际的文件描述符(因为不同进程的fd数值对应的文件表项是独立的,只有传递fd的内核对象才有效)。这个方法需要处理套接字消息结构,实现起来比较繁琐,适合对进程间通信有深入了解的场景。
额外注意点
- 共享内存里存的只是fd的数值,不是文件描述符本身——只有进程实际打开了这个socket,数值对应的fd才有效。
- fork之后,父进程和子进程共享文件表项,但新创建的fd不会自动同步到已有的子进程里。
内容的提问来源于stack exchange,提问作者Ashwin Bhoyar
相关产品推荐
相关产品推荐

