通过关闭本地Socket中断Connect调用是否为标准可行方案?
为阻塞式connect设置超时:用shutdown终止未建立的连接是否合理?
C语言中阻塞式connect()调用会一直阻塞直到连接成功或失败,这个过程可能耗时很久。如果想给connect()设置超时,通过调用shutdown()关闭本地端尚未建立的连接,这种方案是否合理?
以下是实现该思路的示例代码:
#include <arpa/inet.h> #include <stdint.h> #include <string.h> #include <sys/socket.h> #include <future> #include <iostream> void Connect(int sock) { struct sockaddr_in addr = {}; addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.s_addr = inet_addr("X.X.X.X"); // 不会立即返回RST的IP memset(addr.sin_zero, '\0', sizeof addr.sin_zero); connect(sock, (sockaddr*)(&addr), sizeof(sockaddr_in)); } int main() { int sock = socket(PF_INET, SOCK_STREAM, 0); auto f = std::async(std::launch::async, [sock]{ Connect(sock); std::cout << "Connect returned" << std::endl; } ); if (f.wait_for(std::chrono::milliseconds(500)) == std::future_status::timeout) { std::cout << "Connect timeout" << std::endl; shutdown(sock, SHUT_RDWR); // 注释掉这行的话会阻塞 } else { std::cout << "Connect success" << std::endl; } return 0; }
注:该方案测试有效,我想知道这是否是终止connect()操作的标准/良好实现方式(测试时使用了互联网DNS服务器的随机端口)。
回答
这种方案可以实现需求,但并非标准或推荐的最佳实践,具体分析如下:
1. 方案的有效性
你的代码通过异步线程执行阻塞connect(),主线程超时后调用shutdown(sock, SHUT_RDWR),确实会让阻塞在connect()的线程返回(错误码通常为ECONNABORTED或EPIPE),从功能上能终止未完成的连接尝试,测试有效是符合预期的。
2. 存在的问题
- 不必要的线程开销:为单个
connect()操作额外启动线程,在高并发场景下会带来显著的资源消耗,不符合高效设计原则。 - socket无法复用:调用
shutdown()后,该socket进入关闭或半关闭状态,无法再用于后续连接,必须重新创建,增加了资源开销。 - 可移植性隐患:虽然多数POSIX系统支持该逻辑,但不同系统对
shutdown()作用于未完成连接的socket的行为可能存在差异,部分系统可能无法立即唤醒阻塞的connect()。
3. 标准推荐的实现方式
给阻塞connect()设置超时,更常用的标准方法是将socket设为非阻塞模式,结合IO多路复用工具(select/poll/epoll)实现超时控制,步骤如下:
- 调用
fcntl()将socket设置为O_NONBLOCK非阻塞模式; - 调用
connect(),此时会立即返回:- 返回0表示连接立即建立成功;
- 返回-1且错误码为
EINPROGRESS,表示连接正在建立中;
- 使用
select()(或poll/epoll)监听该socket的可写事件,同时设置超时时间; - 超时后通过
getsockopt(sock, SOL_SOCKET, SO_ERROR, ...)检查socket错误状态,判断连接是否成功。
这种方法无需额外线程,效率更高,且符合POSIX标准,可移植性更好。
总结
你的方案能实现需求,但属于“可行但不够优雅”的实现。如果追求标准、高效和可移植性,建议使用非阻塞socket结合IO多路复用的方案。
内容的提问来源于stack exchange,提问作者average joe
相关产品推荐
相关产品推荐

