|
|
@ -58,6 +58,60 @@ 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) {
|
|
|
|
|
|
|
|
// Blocking if no timeout
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set non-blocking
|
|
|
|
|
|
|
|
u_long mode = 1UL;
|
|
|
|
|
|
|
|
::ioctlsocket(sockfd, FIONBIO, &mode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int rv = ::connect(sockfd, addr, addrlen);
|
|
|
|
|
|
|
|
int last_error = WSAGetLastError();
|
|
|
|
|
|
|
|
if (rv == 0 || last_error == WSAEISCONN) {
|
|
|
|
|
|
|
|
mode = 0UL;
|
|
|
|
|
|
|
|
::ioctlsocket(sockfd, FIONBIO, &mode);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_error != WSAEWOULDBLOCK) {
|
|
|
|
|
|
|
|
mode = 0UL;
|
|
|
|
|
|
|
|
::ioctlsocket(sockfd, FIONBIO, &mode);
|
|
|
|
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Wait for writability
|
|
|
|
|
|
|
|
fd_set wfds;
|
|
|
|
|
|
|
|
FD_ZERO(&wfds);
|
|
|
|
|
|
|
|
FD_SET(sockfd, &wfds);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rv = ::select(0, nullptr, &wfds, nullptr, &tv);
|
|
|
|
|
|
|
|
mode = 0UL;
|
|
|
|
|
|
|
|
::ioctlsocket(sockfd, FIONBIO, &mode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rv <= 0) {
|
|
|
|
|
|
|
|
// timeout or error
|
|
|
|
|
|
|
|
if (rv == 0) WSASetLastError(WSAETIMEDOUT);
|
|
|
|
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check socket for errors
|
|
|
|
|
|
|
|
int so_error = 0;
|
|
|
|
|
|
|
|
int len = sizeof(so_error);
|
|
|
|
|
|
|
|
::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len);
|
|
|
|
|
|
|
|
if (so_error != 0 && so_error != WSAEISCONN) {
|
|
|
|
|
|
|
|
WSASetLastError(so_error);
|
|
|
|
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// try to connect or throw on failure
|
|
|
|
// try to connect or throw on failure
|
|
|
|
void connect(const std::string &host, int port, int timeout_ms) {
|
|
|
|
void connect(const std::string &host, int port, int timeout_ms) {
|
|
|
|
if (is_connected()) {
|
|
|
|
if (is_connected()) {
|
|
|
@ -71,6 +125,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 +140,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,47 +147,11 @@ public:
|
|
|
|
WSACleanup();
|
|
|
|
WSACleanup();
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// set non-blocking if timeout specified
|
|
|
|
if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) {
|
|
|
|
u_long mode = (timeout_ms > 0) ? 1UL : 0UL;
|
|
|
|
|
|
|
|
::ioctlsocket(socket_, FIONBIO, &mode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rv = ::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen);
|
|
|
|
|
|
|
|
if (rv == SOCKET_ERROR) {
|
|
|
|
|
|
|
|
last_error = WSAGetLastError();
|
|
|
|
|
|
|
|
if (timeout_ms > 0 && last_error == WSAEWOULDBLOCK) {
|
|
|
|
|
|
|
|
fd_set wfds;
|
|
|
|
|
|
|
|
FD_ZERO(&wfds);
|
|
|
|
|
|
|
|
FD_SET(socket_, &wfds);
|
|
|
|
|
|
|
|
timeval tv;
|
|
|
|
|
|
|
|
tv.tv_sec = timeout_ms / 1000;
|
|
|
|
|
|
|
|
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
|
|
|
|
|
|
|
rv = ::select(0, nullptr, &wfds, nullptr, &tv);
|
|
|
|
|
|
|
|
if (rv > 0) {
|
|
|
|
|
|
|
|
int so_error = 0;
|
|
|
|
|
|
|
|
int len = sizeof(so_error);
|
|
|
|
|
|
|
|
::getsockopt(socket_, SOL_SOCKET, SO_ERROR, (char *)&so_error, &len);
|
|
|
|
|
|
|
|
if (so_error == 0)
|
|
|
|
|
|
|
|
rv = 0;
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
last_error = so_error;
|
|
|
|
|
|
|
|
rv = SOCKET_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
last_error = (rv == 0 ? WSAETIMEDOUT : WSAGetLastError());
|
|
|
|
|
|
|
|
rv = SOCKET_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// restore blocking mode
|
|
|
|
|
|
|
|
mode = 0UL;
|
|
|
|
|
|
|
|
::ioctlsocket(socket_, FIONBIO, &mode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (rv == 0) {
|
|
|
|
|
|
|
|
last_error = 0;
|
|
|
|
last_error = 0;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
last_error = WSAGetLastError();
|
|
|
|
::closesocket(socket_);
|
|
|
|
::closesocket(socket_);
|
|
|
|
socket_ = INVALID_SOCKET;
|
|
|
|
socket_ = INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -139,10 +160,11 @@ public:
|
|
|
|
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);
|
|
|
|
DWORD tv = static_cast<DWORD>(timeout_ms);
|
|
|
|
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
|
|
|
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
|
|
|
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (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;
|
|
|
|