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
11 changes: 10 additions & 1 deletion doc/api/child_process.md
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,9 @@ pipes between the parent and child. The value is one of the following:
file descriptor is duplicated in the child process to the fd that
corresponds to the index in the `stdio` array. The stream must have an
underlying descriptor (file streams do not start until the `'open'` event has
occurred).
occurred). Pipe endpoints returned by [`net.createPipe()`][] may be passed
here. Readable endpoints returned by [`net.createPipe()`][] must not be
flowing when passed here.
**NOTE:** While it is technically possible to pass `stdin` as a writable or
`stdout`/`stderr` as readable, it is not recommended.
Readable and writable streams are designed with distinct behaviors, and using
Expand Down Expand Up @@ -1441,6 +1443,12 @@ streams of a child process have been closed. This is distinct from the
[`'exit'`][] event, since multiple processes might share the same stdio
streams. The `'close'` event will always emit after [`'exit'`][] was
already emitted, or [`'error'`][] if the child process failed to spawn.
Readable stdio streams created by Node.js are resumed after the child process
exits so they can be fully consumed and closed before the `'close'` event is
emitted. Endpoints created by [`net.createPipe()`][] are an exception to this
rule and are not resumed by the child process. Their stream lifecycle remains
owned by the parent process, and consequently the child process `'close'` event
does not wait for such streams to close.

If the process exited, `code` is the final exit code of the process, otherwise
`null`. If the process terminated due to receipt of a signal, `signal` is the
Expand Down Expand Up @@ -2374,6 +2382,7 @@ or [`child_process.fork()`][].
[`maxBuffer` and Unicode]: #maxbuffer-and-unicode
[`net.Server`]: net.md#class-netserver
[`net.Socket`]: net.md#class-netsocket
[`net.createPipe()`]: net.md#netcreatepipe
[`options.detached`]: #optionsdetached
[`process.disconnect()`]: process.md#processdisconnect
[`process.env`]: process.md#processenv
Expand Down
178 changes: 175 additions & 3 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

<!-- source_link=lib/net.js -->

The `node:net` module provides an asynchronous network API for creating stream-based
TCP or [IPC][] servers ([`net.createServer()`][]) and clients
([`net.createConnection()`][]).
The `node:net` module provides an asynchronous network API for creating
stream-based TCP or [IPC][] servers ([`net.createServer()`][]) and clients
([`net.createConnection()`][]), and operating system pipe pairs
([`net.createPipe()`][]) and socket pairs ([`net.createSocketPair()`][]).

It can be accessed using:

Expand Down Expand Up @@ -2382,6 +2383,170 @@ Use `nc` to connect to a Unix domain socket server:
nc -U /tmp/echo.sock
```

Endpoint pairs created by [`net.createPipe()`][] and
[`net.createSocketPair()`][] are owned by the current process. Use normal
stream idioms such as `end()` to finish writing and stream consumption to drain
a readable endpoint. Use `resume()` when an unread readable endpoint should be
drained without observing its data, and use `destroy()` when an endpoint is no
longer needed without being naturally ended or drained.

## `net.createSocketPair()`

<!-- YAML
added: REPLACEME
-->

* Returns: {net.Socket\[]}
* {net.Socket} The first socket.
* {net.Socket} The second socket.

The `net.createSocketPair()` method creates a connected pair of operating
system sockets. The returned [`net.Socket`][] instances are owned by the current
process and may be used to exchange bytes in either direction without binding a
server or connecting a client. Either socket may be passed to a Node.js child
process over an IPC channel using [`subprocess.send()`][].

```cjs
const { spawn } = require('node:child_process');
const { createSocketPair } = require('node:net');

const [left, right] = createSocketPair();

const child = spawn(process.execPath, ['-e', `
process.on('message', (message, socket) => {
socket.on('data', (chunk) => {
socket.write(chunk.toString().toUpperCase());
});
socket.resume();
process.send('ready');
});
`], {
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
});

child.once('message', () => {
left.write('hello');
});

left.once('data', (chunk) => {
console.log(chunk.toString()); // Prints: HELLO
left.destroy();
child.kill();
});

child.send('socket', right, { keepOpen: false });
```

```mjs
import { spawn } from 'node:child_process';
import { createSocketPair } from 'node:net';

const [left, right] = createSocketPair();

const child = spawn(process.execPath, ['-e', `
process.on('message', (message, socket) => {
socket.on('data', (chunk) => {
socket.write(chunk.toString().toUpperCase());
});
socket.resume();
process.send('ready');
});
`], {
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
});

child.once('message', () => {
left.write('hello');
});

left.once('data', (chunk) => {
console.log(chunk.toString()); // Prints: HELLO
left.destroy();
child.kill();
});

child.send('socket', right, { keepOpen: false });
```

## `net.createPipe()`

<!-- YAML
added: REPLACEME
-->

* Returns: {Object}
* `readable` {net.Socket} The readable end of the pipe.
* `writable` {net.Socket} The writable end of the pipe.

The `net.createPipe()` method creates an operating system pipe pair. The
returned `readable` and `writable` streams are owned by the current process and
may be passed to [`child_process.spawn()`][] using the [`stdio`][] option.

When a pipe endpoint is passed to [`child_process.spawn()`][], the child process
leases the endpoint until the child process exits. A pipe endpoint may be leased
to only one child process at a time. Pipe endpoints created by this module are
not supported by synchronous child process APIs such as
[`child_process.spawnSync()`][].

Readable pipe endpoints must not be flowing when they are passed to
[`child_process.spawn()`][]. The child process [`'close'`
event][child-process-close] does not wait for leased endpoints to close and
does not resume them after the child process exits.

When a `readable` endpoint is passed as child stdin or as another child fd, the
child leases a readable handle. When a `writable` endpoint is passed as child
stdout, stderr, or another child fd, the child leases a writable handle. A
`readable` endpoint may not be passed as child stdout or stderr, and a
`writable` endpoint may not be passed as child stdin.

```cjs
const { spawn } = require('node:child_process');
const { createPipe } = require('node:net');
const { text } = require('node:stream/consumers');

const { readable, writable } = createPipe();
const child = spawn(process.execPath, ['-e', `
const fs = require('node:fs');
const buffer = Buffer.alloc(1);
const count = fs.readSync(0, buffer, 0, 1, null);
fs.writeSync(1, buffer.subarray(0, count));
`], {
stdio: [readable, 'pipe', 'inherit'],
});

const output = text(child.stdout);
writable.end('abc');

child.on('close', async () => {
console.log(await output); // Prints: a
console.log(await text(readable)); // Prints: bc
});
```

```mjs
import { spawn } from 'node:child_process';
import { createPipe } from 'node:net';
import { text } from 'node:stream/consumers';

const { readable, writable } = createPipe();
const child = spawn(process.execPath, ['-e', `
const fs = require('node:fs');
const buffer = Buffer.alloc(1);
const count = fs.readSync(0, buffer, 0, 1, null);
fs.writeSync(1, buffer.subarray(0, count));
`], {
stdio: [readable, 'pipe', 'inherit'],
});

const output = text(child.stdout);
writable.end('abc');

child.on('close', async () => {
console.log(await output); // Prints: a
console.log(await text(readable)); // Prints: bc
});
```

## `net.getDefaultAutoSelectFamily()`

<!-- YAML
Expand Down Expand Up @@ -2585,6 +2750,8 @@ console.log('listening on', server.address().port);
[`ERR_SOCKET_HANDLE_ADOPTED`]: errors.md#err_socket_handle_adopted
[`EventEmitter`]: events.md#class-eventemitter
[`child_process.fork()`]: child_process.md#child_processforkmodulepath-args-options
[`child_process.spawn()`]: child_process.md#child_processspawncommand-args-options
[`child_process.spawnSync()`]: child_process.md#child_processspawnsynccommand-args-options
[`dns.lookup()`]: dns.md#dnslookuphostname-options-callback
[`dns.lookup()` hints]: dns.md#supported-getaddrinfo-flags
[`net.Server`]: #class-netserver
Expand All @@ -2597,7 +2764,9 @@ console.log('listening on', server.address().port);
[`net.createConnection(options)`]: #netcreateconnectionoptions-connectlistener
[`net.createConnection(path)`]: #netcreateconnectionpath-connectlistener
[`net.createConnection(port, host)`]: #netcreateconnectionport-host-connectlistener
[`net.createPipe()`]: #netcreatepipe
[`net.createServer()`]: #netcreateserveroptions-connectionlistener
[`net.createSocketPair()`]: #netcreatesocketpair
[`net.getDefaultAutoSelectFamily()`]: #netgetdefaultautoselectfamily
[`net.getDefaultAutoSelectFamilyAttemptTimeout()`]: #netgetdefaultautoselectfamilyattempttimeout
[`netPromises.listen()`]: #netpromiseslistenoptions
Expand All @@ -2612,6 +2781,7 @@ console.log('listening on', server.address().port);
[`server.listen(path)`]: #serverlistenpath-backlog-callback
[`server.listen(port)`]: #serverlistenport-host-backlog-callback
[`server.maxConnections`]: #servermaxconnections
[`subprocess.send()`]: child_process.md#subprocesssendmessage-sendhandle-options-callback
[`socket(7)`]: https://man7.org/linux/man-pages/man7/socket.7.html
[`socket.connect()`]: #socketconnect
[`socket.connect(options)`]: #socketconnectoptions-connectlistener
Expand All @@ -2630,13 +2800,15 @@ console.log('listening on', server.address().port);
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
[`stream.getDefaultHighWaterMark()`]: stream.md#streamgetdefaulthighwatermarkobjectmode
[`stdio`]: child_process.md#optionsstdio
[`worker_threads`]: worker_threads.md
[`writable.destroy()`]: stream.md#writabledestroyerror
[`writable.destroyed`]: stream.md#writabledestroyed
[`writable.end()`]: stream.md#writableendchunk-encoding-callback
[`writable.writableLength`]: stream.md#writablewritablelength
[dot-decimal notation]: https://en.wikipedia.org/wiki/Dot-decimal_notation
[half-closed]: https://tools.ietf.org/html/rfc1122
[child-process-close]: child_process.md#event-close
[stream_writable_write]: stream.md#writablewritechunk-encoding-callback
[unspecified IPv4 address]: https://en.wikipedia.org/wiki/0.0.0.0
[unspecified IPv6 address]: https://en.wikipedia.org/wiki/IPv6_address#Unspecified_address
Loading