From 1d387de17296bd19bf18ef0920f784d814c78102 Mon Sep 17 00:00:00 2001 From: James Hartig Date: Fri, 1 Mar 2013 00:33:38 -0500 Subject: [PATCH] Add keep-alive interval and count options. The default probe interval and failure counts set by the OS are too extreme. Lowering them will allow you to know when clients disconnect sooner, which is helpful if you're running a chat server. --- include/uv.h | 9 ++++++-- src/unix/internal.h | 2 +- src/unix/stream.c | 4 ++-- src/unix/tcp.c | 23 +++++++++++++++----- src/win/tcp.c | 49 ++++++++++++++++++++++++++++++++++--------- test/test-tcp-flags.c | 2 +- 6 files changed, 68 insertions(+), 21 deletions(-) diff --git a/include/uv.h b/include/uv.h index de375d4b0e..5f943798d9 100644 --- a/include/uv.h +++ b/include/uv.h @@ -653,11 +653,16 @@ UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable); /* * Enable/disable TCP keep-alive. * - * `delay` is the initial delay in seconds, ignored when `enable` is zero. + * Options below are ignored when `enable` is zero. + * `delay` is the initial delay in seconds. If 0, value is ignored. + * `interval` is the interval in seconds after initial probe. If 0, value is ignored. + * `count` is the number of failed probes before flagging socket dead. If 0, value is ignored. Unix-only. */ UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, int enable, - unsigned int delay); + unsigned int delay, + unsigned int interval, + unsigned int count); /* * This setting applies to Windows only. diff --git a/src/unix/internal.h b/src/unix/internal.h index 7d08c1dac9..ad80f6aaaa 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -152,7 +152,7 @@ int uv__accept(int sockfd); /* tcp */ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); int uv__tcp_nodelay(int fd, int on); -int uv__tcp_keepalive(int fd, int on, unsigned int delay); +int uv__tcp_keepalive(int fd, int on, unsigned int delay, unsigned int interval, unsigned int count); /* pipe */ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); diff --git a/src/unix/stream.c b/src/unix/stream.c index 080535e5f1..86aba7e345 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -363,8 +363,8 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { if ((stream->flags & UV_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) return uv__set_sys_error(stream->loop, errno); - /* TODO Use delay the user passed in. */ - if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60)) + /* TODO Use delay, interval, count the user passed in. */ + if ((stream->flags & UV_TCP_KEEPALIVE) && uv__tcp_keepalive(fd, 1, 60, 0, 0)) return uv__set_sys_error(stream->loop, errno); } diff --git a/src/unix/tcp.c b/src/unix/tcp.c index a51576ba1b..58ec7be9b1 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -290,12 +290,13 @@ int uv__tcp_nodelay(int fd, int on) { } -int uv__tcp_keepalive(int fd, int on, unsigned int delay) { +int uv__tcp_keepalive(int fd, int on, unsigned int delay, + unsigned int interval, unsigned int count) { if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) return -1; #ifdef TCP_KEEPIDLE - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) + if (on && delay && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) return -1; #endif @@ -303,7 +304,17 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) { * then don't advertise it in your system headers... */ #if defined(TCP_KEEPALIVE) && !defined(__sun) - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) + if (on && delay && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) + return -1; +#endif + +#ifdef TCP_KEEPINTVL + if (on && interval && setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval))) + return -1; +#endif + +#ifdef TCP_KEEPCNT + if (on && count && setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof (count))) return -1; #endif @@ -325,9 +336,10 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int on) { } -int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay, + unsigned int interval, unsigned int count) { if (uv__stream_fd(handle) != -1) - if (uv__tcp_keepalive(uv__stream_fd(handle), on, delay)) + if (uv__tcp_keepalive(uv__stream_fd(handle), on, delay, interval, count)) return -1; if (on) @@ -337,6 +349,7 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge * uv_tcp_t with an int that's almost never used... + * same with interval and count */ return 0; diff --git a/src/win/tcp.c b/src/win/tcp.c index 7158216131..1f3456e357 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -26,6 +26,7 @@ #include "handle-inl.h" #include "stream-inl.h" #include "req-inl.h" +#include "mstcpip.h" /* @@ -57,7 +58,8 @@ 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) { +static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsigned int delay, + unsigned int interval, unsigned int count) { if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, @@ -67,14 +69,41 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign return -1; } - if (enable && setsockopt(socket, - IPPROTO_TCP, - TCP_KEEPALIVE, - (const char*)&delay, - sizeof delay) == -1) { +#ifdef TCP_KEEPALIVE + if (enable && delay && setsockopt(socket, + IPPROTO_TCP, + TCP_KEEPALIVE, + (const char*)&delay, + sizeof delay) == -1) { uv__set_sys_error(handle->loop, errno); return -1; } +#endif + +#ifdef SIO_KEEPALIVE_VALS + if (enable && (delay || interval)) { + struct tcp_keepalive vals; + DWORD outlen = 0; + vals.onoff = 1; + if (!delay) delay = 60 * 60 * 2; /* default is 2 hours */ + if (!interval) interval = 1; /* default is 1 second */ + vals.keepalivetime = delay * 1000; + vals.keepaliveinterval = interval * 1000; + if (WSAIoctl(socket, + SIO_KEEPALIVE_VALS, + &vals, + sizeof(vals), + NULL, + 0, + &outlen, + NULL, + NULL) != 0) { + return -1; + } + } +#endif + + /* count cannot be applied without registry changes and system reboot */ return 0; } @@ -129,9 +158,9 @@ static int uv_tcp_set_socket(uv_loop_t* loop, uv_tcp_t* handle, return -1; } - /* TODO: Use stored delay. */ + /* TODO: Use stored delay, interval, count. */ if ((handle->flags & UV_HANDLE_TCP_KEEPALIVE) && - uv__tcp_keepalive(handle, socket, 1, 60)) { + uv__tcp_keepalive(handle, socket, 1, 60, 0, 0)) { return -1; } @@ -1194,9 +1223,9 @@ 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 enable, unsigned int delay, unsigned int interval, unsigned int count) { if (handle->socket != INVALID_SOCKET && - uv__tcp_keepalive(handle, handle->socket, enable, delay)) { + uv__tcp_keepalive(handle, handle->socket, enable, delay, interval, count)) { return -1; } diff --git a/test/test-tcp-flags.c b/test/test-tcp-flags.c index 68afb39f45..eeff9cc006 100644 --- a/test/test-tcp-flags.c +++ b/test/test-tcp-flags.c @@ -39,7 +39,7 @@ TEST_IMPL(tcp_flags) { r = uv_tcp_nodelay(&handle, 1); ASSERT(r == 0); - r = uv_tcp_keepalive(&handle, 1, 60); + r = uv_tcp_keepalive(&handle, 1, 60, 60, 8); ASSERT(r == 0); uv_close((uv_handle_t*)&handle, NULL);