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是否合理?
解答
临时修复的可行性
这个修复是可行的。在macOS上,当connect返回ECONNREFUSED(61)后,该socket处于不可复用状态——后续再次调用connect会直接返回EINVAL(22),这是BSD系套接字实现的特性(和Linux的行为不同,Linux允许对失败的socket重新调用connect)。重新创建socket可以绕过这个限制,让后续连接尝试正常进行。是否属于标准恢复方式
这是针对BSD/macOS平台的常规兼容处理方式。POSIX标准并没有强制规定ECONNREFUSED后socket是否可复用,不同系统实现有差异:
- Linux允许对失败的socket再次调用
connect - BSD/macOS则会将socket标记为不可用,必须重新创建
因此这种修复是符合平台特性的标准兼容手段,而非“hack”。
- 是否需要额外清理
需要。在替换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是必须的步骤。
- 频繁创建新socket是否合理
在你的场景中(每2秒尝试一次连接),这种频率完全合理。socket是轻量级内核对象,创建和销毁的开销极低,不会对系统性能造成影响。
如果是极端高频的重试(比如毫秒级),可能需要考虑复用socket的方案,但对于秒级重试来说,重新创建socket的成本可以忽略不计。
内容的提问来源于stack exchange,提问作者Kungfunk
相关产品推荐
相关产品推荐

