非阻塞套接字connect
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後會立即返回是否繫結成功,所以沒有必要使用這個方法。