@ -58,8 +58,81 @@ public:
SOCKET fd ( ) const { return socket_ ; }
SOCKET fd ( ) const { return socket_ ; }
int connect_socket_with_timeout ( SOCKET sockfd ,
const struct sockaddr * addr ,
int addrlen ,
const timeval & tv ) {
// If no timeout requested, do a normal blocking connect.
if ( tv . tv_sec = = 0 & & tv . tv_usec = = 0 ) {
int rv = : : connect ( sockfd , addr , addrlen ) ;
if ( rv = = SOCKET_ERROR & & WSAGetLastError ( ) = = WSAEISCONN ) {
return 0 ;
}
return rv ;
}
// Switch to non‐ blocking mode
u_long mode = 1UL ;
if ( : : ioctlsocket ( sockfd , FIONBIO , & mode ) = = SOCKET_ERROR ) {
return SOCKET_ERROR ;
}
int rv = : : connect ( sockfd , addr , addrlen ) ;
int last_error = WSAGetLastError ( ) ;
if ( rv = = 0 | | last_error = = WSAEISCONN ) {
mode = 0UL ;
if ( : : ioctlsocket ( sockfd , FIONBIO , & mode ) = = SOCKET_ERROR ) {
return SOCKET_ERROR ;
}
return 0 ;
}
if ( last_error ! = WSAEWOULDBLOCK ) {
// Real error
mode = 0UL ;
if ( : : ioctlsocket ( sockfd , FIONBIO , & mode ) ) {
return SOCKET_ERROR ;
}
return SOCKET_ERROR ;
}
// Wait until socket is writable or timeout expires
fd_set wfds ;
FD_ZERO ( & wfds ) ;
FD_SET ( sockfd , & wfds ) ;
rv = : : select ( 0 , nullptr , & wfds , nullptr , const_cast < timeval * > ( & tv ) ) ;
// Restore blocking mode regardless of select result
mode = 0UL ;
if ( : : ioctlsocket ( sockfd , FIONBIO , & mode ) = = SOCKET_ERROR ) {
return SOCKET_ERROR ;
}
if ( rv = = 0 ) {
WSASetLastError ( WSAETIMEDOUT ) ;
return SOCKET_ERROR ;
}
if ( rv = = SOCKET_ERROR ) {
return SOCKET_ERROR ;
}
int so_error = 0 ;
int len = sizeof ( so_error ) ;
if ( : : getsockopt ( sockfd , SOL_SOCKET , SO_ERROR , reinterpret_cast < char * > ( & so_error ) , & len ) = =
SOCKET_ERROR ) {
return SOCKET_ERROR ;
}
if ( so_error ! = 0 & & so_error ! = WSAEISCONN ) {
// connection failed
WSASetLastError ( so_error ) ;
return SOCKET_ERROR ;
}
return 0 ; // success
}
// try to connect or throw on failure
// try to connect or throw on failure
void connect ( const std : : string & host , int port ) {
void connect ( const std : : string & host , int port , int timeout_ms = 0 ) {
if ( is_connected ( ) ) {
if ( is_connected ( ) ) {
close ( ) ;
close ( ) ;
}
}
@ -71,6 +144,10 @@ public:
hints . ai_flags = AI_NUMERICSERV ; // port passed as as numeric value
hints . ai_flags = AI_NUMERICSERV ; // port passed as as numeric value
hints . ai_protocol = 0 ;
hints . ai_protocol = 0 ;
timeval tv ;
tv . tv_sec = timeout_ms / 1000 ;
tv . tv_usec = ( timeout_ms % 1000 ) * 1000 ;
auto port_str = std : : to_string ( port ) ;
auto port_str = std : : to_string ( port ) ;
struct addrinfo * addrinfo_result ;
struct addrinfo * addrinfo_result ;
auto rv = : : getaddrinfo ( host . c_str ( ) , port_str . c_str ( ) , & hints , & addrinfo_result ) ;
auto rv = : : getaddrinfo ( host . c_str ( ) , port_str . c_str ( ) , & hints , & addrinfo_result ) ;
@ -82,7 +159,6 @@ public:
}
}
// Try each address until we successfully connect(2).
// Try each address until we successfully connect(2).
for ( auto * rp = addrinfo_result ; rp ! = nullptr ; rp = rp - > ai_next ) {
for ( auto * rp = addrinfo_result ; rp ! = nullptr ; rp = rp - > ai_next ) {
socket_ = socket ( rp - > ai_family , rp - > ai_socktype , rp - > ai_protocol ) ;
socket_ = socket ( rp - > ai_family , rp - > ai_socktype , rp - > ai_protocol ) ;
if ( socket_ = = INVALID_SOCKET ) {
if ( socket_ = = INVALID_SOCKET ) {
@ -90,18 +166,24 @@ public:
WSACleanup ( ) ;
WSACleanup ( ) ;
continue ;
continue ;
}
}
if ( : : connect ( socket_ , rp - > ai_addr , ( int ) rp - > ai_addrlen ) = = 0 ) {
if ( connect_socket_with_timeout ( socket_ , rp - > ai_addr , ( int ) rp - > ai_addrlen , tv ) = = 0 ) {
last_error = 0 ;
break ;
break ;
} else {
last_error = : : WSAGetLastError ( ) ;
close ( ) ;
}
}
last_error = WSAGetLastError ( ) ;
: : closesocket ( socket_ ) ;
socket_ = INVALID_SOCKET ;
}
}
: : freeaddrinfo ( addrinfo_result ) ;
: : freeaddrinfo ( addrinfo_result ) ;
if ( socket_ = = INVALID_SOCKET ) {
if ( socket_ = = INVALID_SOCKET ) {
WSACleanup ( ) ;
WSACleanup ( ) ;
throw_winsock_error_ ( " connect failed " , last_error ) ;
throw_winsock_error_ ( " connect failed " , last_error ) ;
}
}
if ( timeout_ms > 0 ) {
DWORD tv = static_cast < DWORD > ( timeout_ms ) ;
: : setsockopt ( socket_ , SOL_SOCKET , SO_RCVTIMEO , ( const char * ) & tv , sizeof ( tv ) ) ;
: : setsockopt ( socket_ , SOL_SOCKET , SO_SNDTIMEO , ( const char * ) & tv , sizeof ( tv ) ) ;
}
// set TCP_NODELAY
// set TCP_NODELAY
int enable_flag = 1 ;
int enable_flag = 1 ;