diff options
author | Andy Pan <i@andypan.me> | 2025-05-21 14:43:53 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-21 08:43:53 +0200 |
commit | 3a9a6e3e6bc78565ddf94cf462c9877c1004bb62 (patch) | |
tree | d684b2e44f5dc44ac608ca681422dc9f33c142a6 /src/win/tcp.c | |
parent | 71ec5c0fcdd867b64c46ade1e0a6b59101281a4a (diff) | |
download | libuv-main.tar.gz libuv-main.zip |
Implement `uv_tcp_keepalive_ex` function that extends
`uv_tcp_keepalive` to support `TCP_KEEPINTVL` and `TCP_KEEPCN`
socket options in addition to TCP_KEEPIDLE.
Diffstat (limited to 'src/win/tcp.c')
-rw-r--r-- | src/win/tcp.c | 126 |
1 files changed, 95 insertions, 31 deletions
diff --git a/src/win/tcp.c b/src/win/tcp.c index c452c12e..5b7604a9 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -49,29 +49,99 @@ static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) { } -static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay) { +/* + * Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later. + */ +static int minimal_windows10_version1709(void) { + OSVERSIONINFOW os_info; + if (!pRtlGetVersion) + return 0; + pRtlGetVersion(&os_info); + if (os_info.dwMajorVersion < 10) + return 0; + if (os_info.dwMajorVersion > 10) + return 1; + if (os_info.dwMinorVersion > 0) + return 1; + return os_info.dwBuildNumber >= 16299; +} + + +static int uv__tcp_keepalive(uv_tcp_t* handle, + SOCKET socket, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, - (const char*)&enable, - sizeof enable) == -1) { + (const char*)&on, + sizeof on) == -1) { return WSAGetLastError(); } - if (!enable) + if (!on) return 0; - if (delay < 1) + if (idle < 1 || intvl < 1 || cnt < 1) return UV_EINVAL; - if (setsockopt(socket, - IPPROTO_TCP, - TCP_KEEPALIVE, - (const char*)&delay, - sizeof delay) == -1) { - return WSAGetLastError(); + /* Windows 10, version 1709 (build 10.0.16299) and later require second units + * for TCP keepalive options. */ + if (minimal_windows10_version1709()) { + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPIDLE, + (const char*)&idle, + sizeof idle) == -1) { + return WSAGetLastError(); + } + + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPINTVL, + (const char*)&intvl, + sizeof intvl) == -1) { + return WSAGetLastError(); + } + + if (setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPCNT, + (const char*)&cnt, + sizeof cnt) == -1) { + return WSAGetLastError(); + } + + return 0; } + /* For those versions prior to Windows 10 version 1709, + * we fall back to SIO_KEEPALIVE_VALS that expects millisecond units. + * The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000 + * and later versions of the operating system. */ + struct tcp_keepalive keepalive; + keepalive.onoff = on; + keepalive.keepalivetime = idle * 1000; + keepalive.keepaliveinterval = intvl * 1000; + /* On Windows Vista and later, the number of keep-alive probes + * (data retransmissions) is set to 10 and cannot be changed. + * On Windows Server 2003, Windows XP, and Windows 2000, the default setting + * for number of keep-alive probes is 5 and cannot be changed programmatically. + */ + DWORD dummy; + if (WSAIoctl(socket, + SIO_KEEPALIVE_VALS, + (LPVOID) &keepalive, + sizeof keepalive, + NULL, + 0, + &dummy, + NULL, + NULL) == -1) + return WSAGetLastError(); + return 0; } @@ -132,7 +202,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop, /* TODO: Use stored delay. */ if (handle->flags & UV_HANDLE_TCP_KEEPALIVE) { - err = uv__tcp_keepalive(handle, socket, 1, 60); + err = uv__tcp_keepalive(handle, socket, 1, 60, 1, 10); if (err) return err; } @@ -749,20 +819,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) { return 0; } -// Check if Windows version is 10.0.16299 or later -static int uv__is_fast_loopback_fail_supported(void) { - OSVERSIONINFOW os_info; - if (!pRtlGetVersion) - return 0; - pRtlGetVersion(&os_info); - if (os_info.dwMajorVersion < 10) - return 0; - if (os_info.dwMajorVersion > 10) - return 1; - if (os_info.dwMinorVersion > 0) - return 1; - return os_info.dwBuildNumber >= 16299; -} static int uv__tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, @@ -809,7 +865,7 @@ static int uv__tcp_try_connect(uv_connect_t* req, * is not reachable, instead of waiting for 2s. We do not care if this fails. * This only works on Windows version 10.0.16299 and later. */ - if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) { + if (minimal_windows10_version1709() && uv__is_loopback(&converted)) { memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl)); retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; @@ -1335,22 +1391,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) { } -int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int idle) { + return uv_tcp_keepalive_ex(handle, on, idle, 1, 10); +} + +int uv_tcp_keepalive_ex(uv_tcp_t* handle, + int on, + unsigned int idle, + unsigned int intvl, + unsigned int cnt) { int err; if (handle->socket != INVALID_SOCKET) { - err = uv__tcp_keepalive(handle, handle->socket, enable, delay); + err = uv__tcp_keepalive(handle, handle->socket, on, idle, intvl, cnt); if (err) return uv_translate_sys_error(err); } - if (enable) { + if (on) { handle->flags |= UV_HANDLE_TCP_KEEPALIVE; } else { handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; } - /* TODO: Store delay if handle->socket isn't created yet. */ + /* TODO: Store idle if handle->socket isn't created yet. */ return 0; } |