From 4f9b630d4691a536b7f41b0efed5863e57da70f4 Mon Sep 17 00:00:00 2001 From: Mihir Patel <82491937+LowLevelLore@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:13:22 +0530 Subject: [PATCH] I guess testing on windows passes. --- include/spdlog/details/tcp_client-windows.h | 110 ++++++++++++-------- include/spdlog/details/tcp_client.h | 10 +- include/spdlog/sinks/tcp_sink.h | 2 +- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index 4b05acbb..62f80c76 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -58,12 +58,66 @@ public: 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 void connect(const std::string &host, int port, int timeout_ms) { if (is_connected()) { close(); } - struct addrinfo hints{}; + struct addrinfo hints {}; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on @@ -71,6 +125,10 @@ public: hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value 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); struct addrinfo *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). - for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) { socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (socket_ == INVALID_SOCKET) { @@ -90,47 +147,11 @@ public: WSACleanup(); continue; } - // set non-blocking if timeout specified - 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) { + if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) { last_error = 0; break; } - + last_error = WSAGetLastError(); ::closesocket(socket_); socket_ = INVALID_SOCKET; } @@ -139,10 +160,11 @@ public: WSACleanup(); throw_winsock_error_("connect failed", last_error); } - - DWORD tv = static_cast(timeout_ms); - ::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); - ::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)); + if (timeout_ms > 0) { + DWORD tv = static_cast(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 int enable_flag = 1; diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h index 87d292b1..1eaa3cb9 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -106,7 +106,7 @@ public: // try to connect or throw on failure void connect(const std::string &host, int port, int timeout_ms = 0) { close(); - struct addrinfo hints{}; + struct addrinfo hints {}; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on hints.ai_socktype = SOCK_STREAM; // TCP @@ -151,9 +151,11 @@ public: throw_spdlog_ex("::connect failed", last_errno); } - // Set timeouts for send and recv - ::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv)); - ::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv)); + if (timeout_ms > 0) { + // Set timeouts for send and recv + ::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 int enable_flag = 1; diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h index de8b2558..9ed64133 100644 --- a/include/spdlog/sinks/tcp_sink.h +++ b/include/spdlog/sinks/tcp_sink.h @@ -31,7 +31,7 @@ namespace sinks { struct tcp_sink_config { std::string server_host; int server_port; - int timeout_ms = 0; + int timeout_ms = 0; // The timeout for all 3 major socket operations that is connect, send, and recv bool lazy_connect = false; // if true connect on first log call instead of on construction tcp_sink_config(std::string host, int port)