You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何UNIX下accept()因无效文件描述符失败,Winsock却正常?

问题分析与解决方案

1. accept()无效文件描述符失败的原因及修复

核心问题:跨平台错误判断不兼容

Winsock中INVALID_SOCKET定义为(SOCKET)-1,但UNIX系统没有这个宏,直接用client_socket == INVALID_SOCKET判断会出错——UNIX下accept()失败返回-1,若代码未正确定义INVALID_SOCKET,会导致误判为无效文件描述符。此外,cli_addr_size在accept()调用后会被修改,下次循环未重置会导致参数错误。

修复步骤:

  • 统一跨平台错误判断逻辑,通过条件编译区分:
#ifdef _WIN32
#include <winsock2.h>
#define SOCKET_ERR INVALID_SOCKET
#define CLOSE_SOCKET(s) closesocket(s)
#else
#include <unistd.h>
#define SOCKET_ERR -1
#define CLOSE_SOCKET(s) close(s)
#endif
  • 修改accept()的判断逻辑,同时重置cli_addr_size:
while (true)
{
    socklen_t cli_addr_size = sizeof(client_addr); // 每次循环重置大小
    client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &cli_addr_size);
    if (client_socket == SOCKET_ERR)
    {
        fprintf(stderr, "Accept failed\n");
        perror("");
        // UNIX下accept可能因信号中断返回EINTR,可重试而非直接退出
        if (errno != EINTR) return -1;
        else continue;
    }
    // 后续代码...
}

2. fork()优化与僵尸进程避免

当前代码父进程未处理子进程退出状态,会产生僵尸进程(而非孤儿进程,孤儿进程会被init进程收养),占用系统资源。

优雅处理方案:注册SIGCHLD信号处理器

通过捕获子进程退出信号,自动回收子进程资源:

#include <signal.h>
#include <sys/wait.h>

void sigchld_handler(int sig) {
    // 循环回收所有已退出的子进程,避免僵尸进程残留
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

// 在main函数初始化阶段注册信号处理器:
int main() {
    // ... 其他初始化代码 ...
    signal(SIGCHLD, sigchld_handler);
    // ... 后续listen/accept逻辑 ...
}

子进程逻辑补充优化

  • 子进程处理完客户端请求后必须关闭client_socket并退出,避免进入父进程的accept循环:
if (child == 0)
{
    CLOSE_SOCKET(server_socket);
    rd = recv(client_socket, rcvbuf, sizeof(rcvbuf), 0);
    // ... 处理recv/send逻辑 ...
    CLOSE_SOCKET(client_socket); // 关闭客户端socket
    exit(0); // 子进程退出,避免执行父进程循环
}

内容的提问来源于stack exchange,提问作者user14773854

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:23:05