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

Mac系统下Socket连接先报61再报22错误的解决方案咨询

问题描述

在MacOS系统中,执行以下connect调用时,首次尝试会抛出Connection Refused (61)错误,后续即使服务器已就绪监听,仍会抛出Invalid Arguments (22)错误。而在Linux系统中,代码会持续抛出Connection Refused (111)直到服务器启动,之后可成功连接。

struct hostent* hp = gethostbyname(node->host.c_str());
if (hp == NULL) {
    std::ostringstream os;
    os << "Failed to acquire ip address for hostname `" << node->host << "`";
    die(os.str());
}

sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(node->port);
memcpy(&server_address.sin_addr, hp->h_addr_list[0], hp->h_length);

while (true) {
    std::ostringstream msg;
    msg << "Connecting to server " << node->id << ":" << node->host << ":" << node->port;
    info(msg.str());

    int connect_result = connect(
        client_socket,
        (struct sockaddr*)&server_address,
        sizeof(server_address)
    );
    if (connect_result == 0) break;

    if (errno != 111 && errno != 61 && errno != 22) {
        die_with_errno("Unexpected error when connecting to server");
    } else {
        warn_with_errno("Failed to connect");
    }

    std::this_thread::sleep_for(std::chrono::seconds(2));
}
已尝试方案

发现每次遇到Connection Refused (61)错误后重新获取新的socket文件描述符,服务器就绪后连接可成功:

if (errno == 61) {
    client_socket = get_socket();
} else if (errno != 111) {
    die_with_errno("Unexpected error when connecting to server");
}

现咨询:

  • 该临时修复是否可行?
  • 是否为标准恢复方式?
  • 是否需要额外清理?
  • 频繁创建新socket是否合理?
解答
  1. 临时修复的可行性
    这个修复是可行的。在macOS上,当connect返回ECONNREFUSED(61)后,该socket处于不可复用状态——后续再次调用connect会直接返回EINVAL(22),这是BSD系套接字实现的特性(和Linux的行为不同,Linux允许对失败的socket重新调用connect)。重新创建socket可以绕过这个限制,让后续连接尝试正常进行。

  2. 是否属于标准恢复方式
    这是针对BSD/macOS平台的常规兼容处理方式。POSIX标准并没有强制规定ECONNREFUSED后socket是否可复用,不同系统实现有差异:

  • Linux允许对失败的socket再次调用connect
  • BSD/macOS则会将socket标记为不可用,必须重新创建
    因此这种修复是符合平台特性的标准兼容手段,而非“hack”。
  1. 是否需要额外清理
    需要。在替换client_socket之前,必须调用close()关闭旧的文件描述符,否则会造成文件描述符泄漏。修改后的代码应该是:
if (errno == 61) {
    close(client_socket); // 清理旧socket
    client_socket = get_socket();
} else if (errno != 111) {
    die_with_errno("Unexpected error when connecting to server");
}

如果get_socket()内部已经处理了socket创建(比如调用socket()并设置必要选项),则不需要额外操作,但关闭旧fd是必须的步骤。

  1. 频繁创建新socket是否合理
    在你的场景中(每2秒尝试一次连接),这种频率完全合理。socket是轻量级内核对象,创建和销毁的开销极低,不会对系统性能造成影响。
    如果是极端高频的重试(比如毫秒级),可能需要考虑复用socket的方案,但对于秒级重试来说,重新创建socket的成本可以忽略不计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:37:31