@@ -49,29 +49,101 @@ 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+ os_info .dwOSVersionInfoSize = sizeof (os_info );
60+ os_info .szCSDVersion [0 ] = L'\0' ;
61+ pRtlGetVersion (& os_info );
62+ if (os_info .dwMajorVersion < 10 )
63+ return 0 ;
64+ if (os_info .dwMajorVersion > 10 )
65+ return 1 ;
66+ if (os_info .dwMinorVersion > 0 )
67+ return 1 ;
68+ return os_info .dwBuildNumber >= 16299 ;
69+ }
70+
71+
72+ static int uv__tcp_keepalive (uv_tcp_t * handle ,
73+ SOCKET socket ,
74+ int on ,
75+ unsigned int idle ,
76+ unsigned int intvl ,
77+ unsigned int cnt ) {
5378 if (setsockopt (socket ,
5479 SOL_SOCKET ,
5580 SO_KEEPALIVE ,
56- (const char * )& enable ,
57- sizeof enable ) == -1 ) {
81+ (const char * )& on ,
82+ sizeof on ) == -1 ) {
5883 return WSAGetLastError ();
5984 }
6085
61- if (!enable )
86+ if (!on )
6287 return 0 ;
6388
64- if (delay < 1 )
89+ if (idle < 1 || intvl < 1 || cnt < 1 )
6590 return UV_EINVAL ;
6691
67- if (setsockopt (socket ,
68- IPPROTO_TCP ,
69- TCP_KEEPALIVE ,
70- (const char * )& delay ,
71- sizeof delay ) == -1 ) {
72- return WSAGetLastError ();
92+ /* Windows 10, version 1709 (build 10.0.16299) and later require second units
93+ * for TCP keepalive options. */
94+ if (minimal_windows10_version1709 ()) {
95+ if (setsockopt (socket ,
96+ IPPROTO_TCP ,
97+ TCP_KEEPIDLE ,
98+ (const char * )& idle ,
99+ sizeof idle ) == -1 ) {
100+ return WSAGetLastError ();
101+ }
102+
103+ if (setsockopt (socket ,
104+ IPPROTO_TCP ,
105+ TCP_KEEPINTVL ,
106+ (const char * )& intvl ,
107+ sizeof intvl ) == -1 ) {
108+ return WSAGetLastError ();
109+ }
110+
111+ if (setsockopt (socket ,
112+ IPPROTO_TCP ,
113+ TCP_KEEPCNT ,
114+ (const char * )& cnt ,
115+ sizeof cnt ) == -1 ) {
116+ return WSAGetLastError ();
117+ }
118+
119+ return 0 ;
73120 }
74121
122+ /* For those versions prior to Windows 10 version 1709,
123+ * we fall back to SIO_KEEPALIVE_VALS that expects millisecond units.
124+ * The SIO_KEEPALIVE_VALS IOCTL is supported on Windows 2000
125+ * and later versions of the operating system. */
126+ struct tcp_keepalive keepalive ;
127+ keepalive .onoff = on ;
128+ keepalive .keepalivetime = idle * 1000 ;
129+ keepalive .keepaliveinterval = intvl * 1000 ;
130+ /* On Windows Vista and later, the number of keep-alive probes
131+ * (data retransmissions) is set to 10 and cannot be changed.
132+ * On Windows Server 2003, Windows XP, and Windows 2000, the default setting
133+ * for number of keep-alive probes is 5 and cannot be changed programmatically.
134+ */
135+ DWORD dummy ;
136+ if (WSAIoctl (socket ,
137+ SIO_KEEPALIVE_VALS ,
138+ (LPVOID ) & keepalive ,
139+ sizeof keepalive ,
140+ NULL ,
141+ 0 ,
142+ & dummy ,
143+ NULL ,
144+ NULL ) == -1 )
145+ return WSAGetLastError ();
146+
75147 return 0 ;
76148}
77149
@@ -132,7 +204,7 @@ static int uv__tcp_set_socket(uv_loop_t* loop,
132204
133205 /* TODO: Use stored delay. */
134206 if (handle -> flags & UV_HANDLE_TCP_KEEPALIVE ) {
135- err = uv__tcp_keepalive (handle , socket , 1 , 60 );
207+ err = uv__tcp_keepalive (handle , socket , 1 , 60 , 1 , 10 );
136208 if (err )
137209 return err ;
138210 }
@@ -749,20 +821,6 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) {
749821 return 0 ;
750822}
751823
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- }
766824
767825static int uv__tcp_try_connect (uv_connect_t * req ,
768826 uv_tcp_t * handle ,
@@ -809,7 +867,7 @@ static int uv__tcp_try_connect(uv_connect_t* req,
809867 * is not reachable, instead of waiting for 2s. We do not care if this fails.
810868 * This only works on Windows version 10.0.16299 and later.
811869 */
812- if (uv__is_fast_loopback_fail_supported () && uv__is_loopback (& converted )) {
870+ if (minimal_windows10_version1709 () && uv__is_loopback (& converted )) {
813871 memset (& retransmit_ioctl , 0 , sizeof (retransmit_ioctl ));
814872 retransmit_ioctl .Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ;
815873 retransmit_ioctl .MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ;
@@ -1335,22 +1393,30 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) {
13351393}
13361394
13371395
1338- int uv_tcp_keepalive (uv_tcp_t * handle , int enable , unsigned int delay ) {
1396+ int uv_tcp_keepalive (uv_tcp_t * handle , int on , unsigned int idle ) {
1397+ return uv_tcp_keepalive_ex (handle , on , idle , 1 , 10 );
1398+ }
1399+
1400+ int uv_tcp_keepalive_ex (uv_tcp_t * handle ,
1401+ int on ,
1402+ unsigned int idle ,
1403+ unsigned int intvl ,
1404+ unsigned int cnt ) {
13391405 int err ;
13401406
13411407 if (handle -> socket != INVALID_SOCKET ) {
1342- err = uv__tcp_keepalive (handle , handle -> socket , enable , delay );
1408+ err = uv__tcp_keepalive (handle , handle -> socket , on , idle , intvl , cnt );
13431409 if (err )
13441410 return uv_translate_sys_error (err );
13451411 }
13461412
1347- if (enable ) {
1413+ if (on ) {
13481414 handle -> flags |= UV_HANDLE_TCP_KEEPALIVE ;
13491415 } else {
13501416 handle -> flags &= ~UV_HANDLE_TCP_KEEPALIVE ;
13511417 }
13521418
1353- /* TODO: Store delay if handle->socket isn't created yet. */
1419+ /* TODO: Store idle if handle->socket isn't created yet. */
13541420
13551421 return 0 ;
13561422}
0 commit comments