EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).
引用man connect,对于非阻塞套接字调用connect不会立即完成,通常返回错误-1,错误码是EINPROGRESS,我们应该调用select或者poll等待套接字可写,然后使用getsockopt获取错误值,如果等于0就是连接成功。
int connect_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen,
int nsec, int usec)
{
if (-1 == make_socket_nonblocking(fd))
{
return -1;
}
struct timeval timeout;
timeout.tv_sec = nsec;
timeout.tv_usec = usec;
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(fd, &write_set);
int ret = connect(fd, addr, addrlen);
//connect成功
if (ret == 0)
{
return 0;
}
//开始三次握手,但并未完成
else if (ret == -1)
{
//只有返回错误EINPROGRESS才继续处理
if (errno != EINPROGRESS)
{
return -1;
}
//关心非阻塞套接字的可写状态
int count = select(fd+1, NULL, &write_set, NULL, &timeout);
//超过timeout还没有触发,连接超时
if (count == 0)
{
return -1;
}
//select返回错误
if (count == -1)
{
return -1;
}
if (FD_ISSET(fd, &write_set))
{
int err = 0;
socklen_t len = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) == -1)
{
return -1;
}
//err等于0才是连接成功
if (err != 0)
{
return -1;
}
return 0;
}
else
{
return -1;
}
}
}
套接字的各种实现以及非阻塞connect有移植性的问题,可以参考这篇文章非阻塞connect对于select注意问题
connect默认超时时间是75S,但是对于udp调用connect因为并不会引发三次握手,只是内核做了对端的地址和端口绑定,方便后续使用send而不需要使用sendto,connect后会立即返回是否绑定成功,所以没有必要使用这个方法。