问题描述
我使用非阻塞套接字连接到服务器。
在特定的测试场景中,服务器宕机,这意味着TCP SYN出去了,但没有响应,永远无法建立连接。
在此设置中,通常 select
在 2 秒返回 0 后超时。
这是大多数时候的行为,而且似乎是正确的。
然而,在大约 5% 的情况下,select
会立即返回 1(表明套接字在掩码中是可读的)。
但是当我从套接字 read(2)
返回时,-1
会返回 'Network is unreachable
'
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
// sockfd checked and > 0
// set non-blocking
struct timeval tv{};
tv.tv_sec = 2;
int ret = connect(sockfd,addr,addrlen ); // addr set elsewhere
if (ret < 0 && errno == EINPROGRESS)
{
fd_set cset;
FD_ZERO(&cset);
FD_SET(sockfd,&cset);
ret = select(sockfd + 1,&cset,nullptr,&tv);
// returns 1 sometimes
}
在第一篇文章中,我错误地说明了在错误情况下,网络上只有一个 TCP SYN(没有重试)。
这不是真的;错误和非错误情况下,网络上都有一个TCP SYN在1秒后重新发送。
可能导致这种情况的原因是什么?有没有办法通过 select
获得一致的行为?
解决方法
确定非阻塞 connect()
是否完成的正确方法是询问 select()
可写性而不是可读性。这在 connect()
documentation 中有明确说明:
EINPROGRESS
套接字是非阻塞的,连接不能立即完成。 (UNIX 域套接字失败,而是使用 EAGAIN
。)可以通过选择要写入的套接字来 select(2)
或 poll(2)
来完成。 select(2)
表示可写性后,使用getsockopt(2)
读取SO_ERROR
级别的SOL_SOCKET
选项,判断connect()
是否成功完成({ {1}} 为零)或不成功(SO_ERROR
是此处列出的常见错误代码之一,用于解释失败的原因)。
在您知道连接实际上已经首先建立之前,使用 SO_ERROR
/select()
测试套接字的可读性是未定义的行为。
试试这个:
poll()