Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -1380,11 +1380,15 @@ added: v0.1.90
Set the encoding for the socket as a [Readable Stream][]. See
[`readable.setEncoding()`][] for more information.

### `socket.setKeepAlive([enable][, initialDelay])`
### `socket.setKeepAlive([enable][, initialDelay][, interval][, count])`

<!-- YAML
added: v0.1.92
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/63825
description: Added the `interval` and `count` arguments to configure
`TCP_KEEPINTVL` and `TCP_KEEPCNT`.
- version:
- v13.12.0
- v12.17.0
Expand All @@ -1394,6 +1398,8 @@ changes:

* `enable` {boolean} **Default:** `false`
* `initialDelay` {number} **Default:** `0`
* `interval` {number} **Default:** `1000`
* `count` {number} **Default:** `10`
* Returns: {net.Socket} The socket itself.

Enable/disable keep-alive functionality, and optionally set the initial
Expand All @@ -1404,12 +1410,28 @@ data packet received and the first keepalive probe. Setting `0` for
`initialDelay` will leave the value unchanged from the default
(or previous) setting.

Set `interval` (in milliseconds) to set the delay between successive
keepalive probes once they begin (`TCP_KEEPINTVL`). Set `count` to the
number of unacknowledged probes sent before the connection is dropped
(`TCP_KEEPCNT`). Both are only applied when keep-alive is enabled.
Omitting `interval` or `count` uses the defaults of `1000` ms and `10`.
As with `initialDelay`, a non-positive `interval` or `count` leaves the
corresponding system default unchanged.

`initialDelay` and `interval` are specified in milliseconds but the
underlying socket options are configured in whole seconds; the values are
divided by `1000` and rounded down before being applied.

Enabling the keep-alive functionality will set the following socket options:

* `SO_KEEPALIVE=1`
* `TCP_KEEPIDLE=initialDelay`
* `TCP_KEEPCNT=10`
* `TCP_KEEPINTVL=1`
* `TCP_KEEPIDLE=initialDelay / 1000`
* `TCP_KEEPCNT=count`
* `TCP_KEEPINTVL=interval / 1000`

On Windows versions older than build 1709, keep-alive is configured through
`SIO_KEEPALIVE_VALS`, which has no probe-count field, so `count` is ignored on
those platforms.

### `socket.setNoDelay([noDelay])`

Expand Down
2 changes: 2 additions & 0 deletions lib/internal/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ module.exports = {
kSetNoDelay: Symbol('kSetNoDelay'),
kSetKeepAlive: Symbol('kSetKeepAlive'),
kSetKeepAliveInitialDelay: Symbol('kSetKeepAliveInitialDelay'),
kSetKeepAliveInterval: Symbol('kSetKeepAliveInterval'),
kSetKeepAliveCount: Symbol('kSetKeepAliveCount'),
isIP,
isIPv4,
isIPv6,
Expand Down
21 changes: 17 additions & 4 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const {
kSetNoDelay,
kSetKeepAlive,
kSetKeepAliveInitialDelay,
kSetKeepAliveInterval,
kSetKeepAliveCount,
isIP,
isIPv4,
isIPv6,
Expand Down Expand Up @@ -629,13 +631,18 @@ Socket.prototype.setNoDelay = function(enable) {
};


Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs,
intervalMsecs, count) {
enable = Boolean(enable);
const initialDelay = ~~(initialDelayMsecs / 1000);
const interval = intervalMsecs === undefined ?
undefined : ~~(intervalMsecs / 1000);

if (!this._handle) {
this[kSetKeepAlive] = enable;
this[kSetKeepAliveInitialDelay] = initialDelay;
this[kSetKeepAliveInterval] = interval;
this[kSetKeepAliveCount] = count;
return this;
}

Expand All @@ -646,12 +653,16 @@ Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
if (enable !== this[kSetKeepAlive] ||
(
enable &&
this[kSetKeepAliveInitialDelay] !== initialDelay
(this[kSetKeepAliveInitialDelay] !== initialDelay ||
this[kSetKeepAliveInterval] !== interval ||
this[kSetKeepAliveCount] !== count)
)
) {
this[kSetKeepAlive] = enable;
this[kSetKeepAliveInitialDelay] = initialDelay;
this._handle.setKeepAlive(enable, initialDelay);
this[kSetKeepAliveInterval] = interval;
this[kSetKeepAliveCount] = count;
this._handle.setKeepAlive(enable, initialDelay, interval, count);
}

return this;
Expand Down Expand Up @@ -1675,7 +1686,9 @@ function afterConnect(status, handle, req, readable, writable) {
}

if (self[kSetKeepAlive] && self._handle.setKeepAlive) {
self._handle.setKeepAlive(true, self[kSetKeepAliveInitialDelay]);
self._handle.setKeepAlive(true, self[kSetKeepAliveInitialDelay],
self[kSetKeepAliveInterval],
self[kSetKeepAliveCount]);
}

if (self[kSetTOS] !== undefined && self._handle.setTypeOfService) {
Expand Down
9 changes: 8 additions & 1 deletion src/tcp_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,14 @@ void TCPWrap::SetKeepAlive(const FunctionCallbackInfo<Value>& args) {
int enable;
if (!args[0]->Int32Value(env->context()).To(&enable)) return;
unsigned int delay = static_cast<unsigned int>(args[1].As<Uint32>()->Value());
int err = uv_tcp_keepalive(&wrap->handle_, enable, delay);
// interval and count are optional. Fall back to the libuv defaults
// (1 second, 10 probes) when they are not provided so that callers using
// the legacy two-argument form of this handle method keep working.
unsigned int interval = 1;
unsigned int count = 10;
if (args[2]->IsUint32()) interval = args[2].As<Uint32>()->Value();
if (args[3]->IsUint32()) count = args[3].As<Uint32>()->Value();
int err = uv_tcp_keepalive_ex(&wrap->handle_, enable, delay, interval, count);
args.GetReturnValue().Set(err);
}

Expand Down
56 changes: 56 additions & 0 deletions test/parallel/test-net-keepalive-interval-count.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const net = require('net');

// Verifies that setKeepAlive() forwards the keepalive probe interval
// (TCP_KEEPINTVL) and probe count (TCP_KEEPCNT) to the handle. The interval is
// converted from milliseconds to whole seconds, mirroring the initialDelay
// handling and uv_tcp_keepalive_ex().

// Explicit setKeepAlive(enable, initialDelay, interval, count) forwards all
// four values, converting milliseconds to seconds for the delays.
{
const server = net.createServer();
server.listen(0, common.mustCall(() => {
const client = net.connect(
{ port: server.address().port },
common.mustCall(() => {
client._handle.setKeepAlive = common.mustCall(
(enable, delay, interval, count) => {
assert.strictEqual(enable, true);
assert.strictEqual(delay, 5);
assert.strictEqual(interval, 10);
assert.strictEqual(count, 9);
});
client.setKeepAlive(true, 5000, 10000, 9);
client.end();
}));

client.on('end', common.mustCall(() => server.close()));
}));
}

// Omitting interval/count passes undefined through; the handle applies the
// libuv defaults (1s interval, 10 probes).
{
const server = net.createServer();
server.listen(0, common.mustCall(() => {
const client = net.connect(
{ port: server.address().port },
common.mustCall(() => {
client._handle.setKeepAlive = common.mustCall(
(enable, delay, interval, count) => {
assert.strictEqual(enable, true);
assert.strictEqual(delay, 5);
assert.strictEqual(interval, undefined);
assert.strictEqual(count, undefined);
});
client.setKeepAlive(true, 5000);
client.end();
}));

client.on('end', common.mustCall(() => server.close()));
}));
}
Loading