|
72 | 72 | #include "ScopedPtr.h" |
73 | 73 | #include <common/Macros.h> |
74 | 74 |
|
75 | | -#ifndef __WINDOWS__ |
76 | | -#include <fcntl.h> // FD_CLOEXEC |
| 75 | +#ifdef __WINDOWS__ |
| 76 | + // SIO_KEEPALIVE_VALS + struct tcp_keepalive for SetTcpKeepalive. |
| 77 | + // winsock2.h is already brought in transitively by boost::asio. |
| 78 | + #include <mstcpip.h> |
| 79 | +#else |
| 80 | + #include <fcntl.h> // FD_CLOEXEC |
| 81 | + #include <netinet/tcp.h> // TCP_KEEPIDLE / TCP_KEEPINTVL / TCP_KEEPCNT |
| 82 | + #include <sys/socket.h> // SO_KEEPALIVE |
77 | 83 | #endif |
78 | 84 |
|
79 | 85 | using namespace boost::asio; |
@@ -104,6 +110,56 @@ static inline void SetCloexecOnSocket(Handle native) |
104 | 110 | #endif |
105 | 111 | } |
106 | 112 |
|
| 113 | + |
| 114 | +// Turn on TCP keepalive with per-socket timings. Used by the EC sockets |
| 115 | +// to detect a half-open connection (peer gone, FIN/RST lost or never |
| 116 | +// sent — common after a network blip, OOM-kill, etc.) instead of |
| 117 | +// sitting idle until the default ~2h TCP retransmit timeout kicks in. |
| 118 | +// |
| 119 | +// POSIX: SO_KEEPALIVE plus the three TCP-layer timing knobs. Linux |
| 120 | +// names (TCP_KEEPIDLE / TCP_KEEPINTVL / TCP_KEEPCNT) are the canonical |
| 121 | +// set; macOS / *BSD use TCP_KEEPALIVE for the idle time and inherit |
| 122 | +// the system defaults for interval and count, which is acceptable as |
| 123 | +// a fallback. |
| 124 | +// |
| 125 | +// Windows: SIO_KEEPALIVE_VALS via WSAIoctl. The Windows surface only |
| 126 | +// exposes idle and interval; the probe count uses the system default |
| 127 | +// (typically 10 on modern Windows). |
| 128 | +template <typename Handle> |
| 129 | +static inline void SetTcpKeepalive(Handle native, int idleSec, int intervalSec, int count) |
| 130 | +{ |
| 131 | +#ifdef __WINDOWS__ |
| 132 | + struct tcp_keepalive ka = {}; |
| 133 | + ka.onoff = 1; |
| 134 | + ka.keepalivetime = static_cast<ULONG>(idleSec) * 1000; |
| 135 | + ka.keepaliveinterval = static_cast<ULONG>(intervalSec) * 1000; |
| 136 | + DWORD bytesReturned = 0; |
| 137 | + (void) count; // SIO_KEEPALIVE_VALS doesn't expose count |
| 138 | + ::WSAIoctl(native, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), |
| 139 | + NULL, 0, &bytesReturned, NULL, NULL); |
| 140 | +#else |
| 141 | + int yes = 1; |
| 142 | + ::setsockopt(native, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); |
| 143 | + #ifdef TCP_KEEPIDLE |
| 144 | + ::setsockopt(native, IPPROTO_TCP, TCP_KEEPIDLE, &idleSec, sizeof(idleSec)); |
| 145 | + #elif defined(TCP_KEEPALIVE) |
| 146 | + // macOS / *BSD spelling — idle-only, no separate INTVL/CNT knobs. |
| 147 | + ::setsockopt(native, IPPROTO_TCP, TCP_KEEPALIVE, &idleSec, sizeof(idleSec)); |
| 148 | + #endif |
| 149 | + #ifdef TCP_KEEPINTVL |
| 150 | + ::setsockopt(native, IPPROTO_TCP, TCP_KEEPINTVL, &intervalSec, sizeof(intervalSec)); |
| 151 | + #else |
| 152 | + (void) intervalSec; |
| 153 | + #endif |
| 154 | + #ifdef TCP_KEEPCNT |
| 155 | + ::setsockopt(native, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count)); |
| 156 | + #else |
| 157 | + (void) count; |
| 158 | + #endif |
| 159 | +#endif |
| 160 | +} |
| 161 | + |
| 162 | + |
107 | 163 | // Number of threads in the Asio thread pool |
108 | 164 | const int CAsioService::m_numberOfThreads = 4; |
109 | 165 |
|
@@ -228,6 +284,17 @@ class CAsioSocketImpl : public std::enable_shared_from_this<CAsioSocketImpl> |
228 | 284 | return m_OK; |
229 | 285 | } |
230 | 286 |
|
| 287 | + // Apply TCP keepalive timings to the underlying socket if it's open. |
| 288 | + // Caller is expected to invoke this after a successful connect (client |
| 289 | + // side) or accept (server side) so the kernel native_handle is live. |
| 290 | + void EnableTcpKeepalive(int idleSec, int probeIntervalSec, int probeCount) |
| 291 | + { |
| 292 | + if (!m_socket || !m_socket->is_open()) { |
| 293 | + return; |
| 294 | + } |
| 295 | + SetTcpKeepalive(m_socket->native_handle(), idleSec, probeIntervalSec, probeCount); |
| 296 | + } |
| 297 | + |
231 | 298 | bool IsDestroying() const |
232 | 299 | { |
233 | 300 | return m_destroying.load(std::memory_order_acquire); |
@@ -748,6 +815,12 @@ bool CLibSocket::IsOk() const |
748 | 815 | } |
749 | 816 |
|
750 | 817 |
|
| 818 | +void CLibSocket::EnableTcpKeepalive(int idleSec, int probeIntervalSec, int probeCount) |
| 819 | +{ |
| 820 | + m_aSocket->EnableTcpKeepalive(idleSec, probeIntervalSec, probeCount); |
| 821 | +} |
| 822 | + |
| 823 | + |
751 | 824 | wxString CLibSocket::GetPeer() |
752 | 825 | { |
753 | 826 | return m_aSocket->GetPeer(); |
|
0 commit comments