@@ -49,29 +49,99 @@ static int uv__tcp_nodelay(uv_tcp_t* handle, SOCKET socket, int enable) {
4949}
5050
5151
52- static int uv__tcp_keepalive (uv_tcp_t * handle , SOCKET socket , int enable , unsigned int delay ) {
52+ /*
53+ * Check if Windows version is 10.0.16299 (Windows 10, version 1709) or later.
54+ */
55+ static int minimal_windows10_version1709 (void ) {
56+ OSVERSIONINFOW os_info ;
57+ if (!pRtlGetVersion )
58+ return 0 ;
59+ pRtlGetVersion (& os_info );
60+ if (os_info .dwMajorVersion < 10 )
61+ return 0 ;
62+ if (os_info .dwMajorVersion > 10 )
63+ return 1 ;
64+ if (os_info .dwMinorVersion > 0 )
65+ return 1 ;
66+ return os_info .dwBuildNumber >= 16299 ;
67+ }
68+
69+
70+ static int uv__tcp_keepalive (uv_tcp_t * handle ,
71+ SOCKET socket ,
72+ int on ,
73+ unsigned int idle ,
74+ unsigned int intvl ,
75+ unsigned int cnt ) {
5376 if (setsockopt (socket ,
5477 SOL_SOCKET ,
5578 SO_KEEPALIVE ,
56- (const char * )& enable ,
57- sizeof enable ) == -1 ) {
79+ (const char * )& on ,
80+ sizeof on ) == -1 ) {
5881 return WSAGetLastError ();
5982 }
6083
61- if (!enable )
84+ if (!on )
6285 return 0 ;
6386
64- if (delay < 1 )
87+ if (idle < 1 || intvl < 1 || cnt < 1 )
6588 return UV_EINVAL ;
6689
67- if (setsockopt (socket ,
68- IPPROTO_TCP ,
69- TCP_KEEPALIVE ,
70- (const char * )& delay ,
71- sizeof delay ) == -1 ) {
72- return WSAGetLastError ();
90+ /* Windows 10, version 1709 (build 10.0.16299) and later require second units
91+ * for TCP keepalive options. */
92+ if (minimal_windows10_version1709 ()) {
93+ if (setsockopt (socket ,
94+ IPPROTO_TCP ,
95+ TCP_KEEPIDLE ,
96+ (const char * )& idle ,
97+ sizeof idle ) == -1 ) {
98+ return WSAGetLastError ();
99+ }
100+
101+ if (setsockopt (socket ,
102+ IPPROTO_TCP ,
103+ TCP_KEEPINTVL ,
104+ (const char * )& intvl ,
105+ sizeof intvl ) == -1 ) {
106+ return WSAGetLastError ();
107+ }
108+
109+ if (setsockopt (socket ,
110+ IPPROTO_TCP ,
111+ TCP_KEEPCNT ,
112+ (const char * )& cnt ,
113+ sizeof cnt ) == -1 ) {
114+ return WSAGetLastError ();
115+ }
116+
117+ return 0 ;
73118 }
74119
120+ /* For those versions prior to Windows 10 version 1709,
121+ * we fall back to SIO_KEEPALIVE_VALS that expects millisecond units.
122+ * The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000
123+ * and later versions of the operating system. */
124+ struct tcp_keepalive keepalive ;
125+ keepalive .onoff = on ;
126+ keepalive .keepalivetime = idle * 1000 ;
127+ keepalive .keepaliveinterval = intvl * 1000 ;
128+ /* On Windows Vista and later, the number of keep-alive probes
129+ * (data retransmissions) is set to 10 and cannot be changed.
130+ * On Windows Server 2003, Windows XP, and Windows 2000, the default setting
131+ * for number of keep-alive probes is 5 and cannot be changed programmatically.
132+ */
133+ DWORD dummy ;
134+ if (WSAIoctl (socket ,
135+ SIO_KEEPALIVE_VALS ,
136+ (LPVOID ) & keepalive ,
137+ sizeof keepalive ,
138+ NULL ,
139+ 0 ,
140+ & dummy ,
141+ NULL ,
142+ NULL ) == -1 )
143+ return WSAGetLastError ();
144+
75145 return 0 ;
76146}
77147
@@ -132,7 +202,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop,
132202
133203 /* TODO: Use stored delay. */
134204 if (handle -> flags & UV_HANDLE_TCP_KEEPALIVE ) {
135- err = uv__tcp_keepalive (handle , socket , 1 , 60 );
205+ err = uv__tcp_keepalive (handle , socket , 1 , 60 , 1 , 10 );
136206 if (err )
137207 return err ;
138208 }
@@ -749,20 +819,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) {
749819 return 0 ;
750820}
751821
752- // Check if Windows version is 10.0.16299 or later
753- static int uv__is_fast_loopback_fail_supported (void ) {
754- OSVERSIONINFOW os_info ;
755- if (!pRtlGetVersion )
756- return 0 ;
757- pRtlGetVersion (& os_info );
758- if (os_info .dwMajorVersion < 10 )
759- return 0 ;
760- if (os_info .dwMajorVersion > 10 )
761- return 1 ;
762- if (os_info .dwMinorVersion > 0 )
763- return 1 ;
764- return os_info .dwBuildNumber >= 16299 ;
765- }
766822
767823static int uv__tcp_try_connect (uv_connect_t * req ,
768824 uv_tcp_t * handle ,
@@ -809,7 +865,7 @@ static int uv__tcp_try_connect(uv_connect_t* req,
809865 * is not reachable, instead of waiting for 2s. We do not care if this fails.
810866 * This only works on Windows version 10.0.16299 and later.
811867 */
812- if (uv__is_fast_loopback_fail_supported () && uv__is_loopback (& converted )) {
868+ if (minimal_windows10_version1709 () && uv__is_loopback (& converted )) {
813869 memset (& retransmit_ioctl , 0 , sizeof (retransmit_ioctl ));
814870 retransmit_ioctl .Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ;
815871 retransmit_ioctl .MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ;
@@ -1335,22 +1391,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
13351391}
13361392
13371393
1338- int uv_tcp_keepalive (uv_tcp_t * handle , int enable , unsigned int delay ) {
1394+ int uv_tcp_keepalive (uv_tcp_t * handle , int on , unsigned int idle ) {
1395+ return uv_tcp_keepalive_ex (handle , on , idle , 1 , 10 );
1396+ }
1397+
1398+ int uv_tcp_keepalive_ex (uv_tcp_t * handle ,
1399+ int on ,
1400+ unsigned int idle ,
1401+ unsigned int intvl ,
1402+ unsigned int cnt ) {
13391403 int err ;
13401404
13411405 if (handle -> socket != INVALID_SOCKET ) {
1342- err = uv__tcp_keepalive (handle , handle -> socket , enable , delay );
1406+ err = uv__tcp_keepalive (handle , handle -> socket , on , idle , intvl , cnt );
13431407 if (err )
13441408 return uv_translate_sys_error (err );
13451409 }
13461410
1347- if (enable ) {
1411+ if (on ) {
13481412 handle -> flags |= UV_HANDLE_TCP_KEEPALIVE ;
13491413 } else {
13501414 handle -> flags &= ~UV_HANDLE_TCP_KEEPALIVE ;
13511415 }
13521416
1353- /* TODO: Store delay if handle->socket isn't created yet. */
1417+ /* TODO: Store idle if handle->socket isn't created yet. */
13541418
13551419 return 0 ;
13561420}
0 commit comments