Skip to content

Commit

Permalink
dgram: add support for UDP connected sockets
Browse files Browse the repository at this point in the history
Added the `dgram.connect()` and `dgram.disconnect()` methods that
associate/disassociate a udp socket to/from a remote address.
It optimizes for cases where lots of packets are sent to the same
address.
Also added the `dgram.remoteAddress()` method to retrieve the associated
remote address.

PR-URL: nodejs#26871
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
  • Loading branch information
santigimeno authored and danbev committed Apr 3, 2019
1 parent 38f0e38 commit 9e96017
Show file tree
Hide file tree
Showing 18 changed files with 736 additions and 63 deletions.
75 changes: 72 additions & 3 deletions doc/api/dgram.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ added: v0.1.99
The `'close'` event is emitted after a socket is closed with [`close()`][].
Once triggered, no new `'message'` events will be emitted on this socket.

### Event: 'connect'
<!-- YAML
added: REPLACEME
-->

The `'connect'` event is emitted after a socket is associated to a remote
address as a result of a successful [`connect()`][] call.

### Event: 'error'
<!-- YAML
added: v0.1.99
Expand Down Expand Up @@ -237,6 +245,34 @@ added: v0.1.99
Close the underlying socket and stop listening for data on it. If a callback is
provided, it is added as a listener for the [`'close'`][] event.

### socket.connect(port[, address][, callback])
<!-- YAML
added: REPLACEME
-->

* `port` {integer}
* `address` {string}
* `callback` {Function} Called when the connection is completed or on error.

Associates the `dgram.Socket` to a remote address and port. Every
message sent by this handle is automatically sent to that destination. Also,
the socket will only receive messages from that remote peer.
Trying to call `connect()` on an already connected socket will result
in an [`ERR_SOCKET_DGRAM_IS_CONNECTED`][] exception. If `address` is not
provided, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for `udp6` sockets)
will be used by default. Once the connection is complete, a `'connect'` event
is emitted and the optional `callback` function is called. In case of failure,
the `callback` is called or, failing this, an `'error'` event is emitted.

### socket.disconnect()
<!-- YAML
added: REPLACEME
-->

A synchronous function that disassociates a connected `dgram.Socket` from
its remote address. Trying to call `disconnect()` on an already disconnected
socket will result in an [`ERR_SOCKET_DGRAM_NOT_CONNECTED`][] exception.

### socket.dropMembership(multicastAddress[, multicastInterface])
<!-- YAML
added: v0.6.9
Expand Down Expand Up @@ -283,7 +319,18 @@ Calling `socket.ref()` multiples times will have no additional effect.
The `socket.ref()` method returns a reference to the socket so calls can be
chained.

### socket.send(msg[, offset, length], port[, address][, callback])
### socket.remoteAddress()
<!-- YAML
added: REPLACEME
-->

* Returns: {Object}

Returns an object containing the `address`, `family`, and `port` of the remote
endpoint. It throws an [`ERR_SOCKET_DGRAM_NOT_CONNECTED`][] exception if the
socket is not connected.

### socket.send(msg[, offset, length][, port][, address][, callback])
<!-- YAML
added: v0.1.99
changes:
Expand All @@ -301,6 +348,9 @@ changes:
pr-url: https://github.com/nodejs/node/pull/4374
description: The `msg` parameter can be an array now. Also, the `offset`
and `length` parameters are optional now.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/26871
description: Added support for sending data on connected sockets.
-->

* `msg` {Buffer|Uint8Array|string|Array} Message to be sent.
Expand All @@ -310,8 +360,10 @@ changes:
* `address` {string} Destination hostname or IP address.
* `callback` {Function} Called when the message has been sent.

Broadcasts a datagram on the socket. The destination `port` and `address` must
be specified.
Broadcasts a datagram on the socket.
For connectionless sockets, the destination `port` and `address` must be
specified. Connected sockets, on the other hand, will use their associated
remote endpoint, so the `port` and `address` arguments must not be set.

The `msg` argument contains the message to be sent.
Depending on its type, different behavior can apply. If `msg` is a `Buffer`
Expand Down Expand Up @@ -375,6 +427,20 @@ application and operating system. It is important to run benchmarks to
determine the optimal strategy on a case-by-case basis. Generally speaking,
however, sending multiple buffers is faster.

Example of sending a UDP packet using a socket connected to a port on
`localhost`:

```js
const dgram = require('dgram');
const message = Buffer.from('Some bytes');
const client = dgram.createSocket('udp4');
client.connect(41234, 'localhost', (err) => {
client.send(message, (err) => {
client.close();
});
});
```

**A Note about UDP datagram size**

The maximum size of an `IPv4/v6` datagram depends on the `MTU`
Expand Down Expand Up @@ -651,10 +717,13 @@ and `udp6` sockets). The bound address and port can be retrieved using

[`'close'`]: #dgram_event_close
[`Error`]: errors.html#errors_class_error
[`ERR_SOCKET_DGRAM_IS_CONNECTED`]: errors.html#errors_err_socket_dgram_is_connected
[`ERR_SOCKET_DGRAM_NOT_CONNECTED`]: errors.html#errors_err_socket_dgram_not_connected
[`EventEmitter`]: events.html
[`System Error`]: errors.html#errors_class_systemerror
[`close()`]: #dgram_socket_close_callback
[`cluster`]: cluster.html
[`connect()`]: #dgram_socket_connect_port_address_callback
[`dgram.Socket#bind()`]: #dgram_socket_bind_options_callback
[`dgram.createSocket()`]: #dgram_dgram_createsocket_options_callback
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
Expand Down
14 changes: 14 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,17 @@ Data could be sent on a socket.

An attempt was made to operate on an already closed socket.

<a id="ERR_SOCKET_DGRAM_IS_CONNECTED"></a>
### ERR_SOCKET_DGRAM_IS_CONNECTED

A [`dgram.connect()`][] call was made on an already connected socket.

<a id="ERR_SOCKET_DGRAM_NOT_CONNECTED"></a>
### ERR_SOCKET_DGRAM_NOT_CONNECTED

A [`dgram.disconnect()`][] or [`dgram.remoteAddress()`][] call was made on a
disconnected socket.

<a id="ERR_SOCKET_DGRAM_NOT_RUNNING"></a>
### ERR_SOCKET_DGRAM_NOT_RUNNING

Expand Down Expand Up @@ -2288,7 +2299,10 @@ such as `process.stdout.on('data')`.
[`crypto.scrypt()`]: crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback
[`crypto.scryptSync()`]: crypto.html#crypto_crypto_scryptsync_password_salt_keylen_options
[`crypto.timingSafeEqual()`]: crypto.html#crypto_crypto_timingsafeequal_a_b
[`dgram.connect()`]: dgram.html#dgram_socket_connect_port_address_callback
[`dgram.createSocket()`]: dgram.html#dgram_dgram_createsocket_options_callback
[`dgram.disconnect()`]: dgram.html#dgram_socket_disconnect
[`dgram.remoteAddress()`]: dgram.html#dgram_socket_remoteaddress
[`errno`(3) man page]: http://man7.org/linux/man-pages/man3/errno.3.html
[`fs.readFileSync`]: fs.html#fs_fs_readfilesync_path_options
[`fs.readdir`]: fs.html#fs_fs_readdir_path_options_callback
Expand Down
Loading

0 comments on commit 9e96017

Please sign in to comment.