net module

The net module gives us access to lower-level socket systems and even to local InterProcess Communication (IPC) schemes that we can use. IPC schemes are communication strategies that allow us to talk between processes. Processes don't share memory, which means that we have to communicate through other means. In Node.js, this usually means three different strategies, and they all depend on how quickly and how tightly coupled we want the systems to be. These three strategies are as follows:

First, we have unnamed pipes. These are one-way communication systems that are not seen on the filesystem and are shared between a parent and a child process. This means that a parent process would spawn a child process and parent would pass the location of one end of the pipe to child. By doing this, they would be able to communicate over this channel. An example of this is as follows:

import { fork } from 'child_process';

const child = fork('child.js');
child.on('message', (msg) => {
switch(msg) {
case 'CONNECT': {
console.log('our child is connected to us. Tell it to dispose
of itself');
child.send('DISCONNECT');
break;
}
case 'DISCONNECT': {
console.log('our child is disposing of itself. Time for us to
do the same');
process.exit();
break;
}
}
});

Our child file will look as follows:

process.on('message', (msg) => {
switch(msg) {
case 'DISCONNECT': {
process.exit();
break;
}
}
});
process.send('CONNECT');

We grab the fork method from the child_process module (this allows us to spawn new processes). Then, we fork a new child off of the child JavaScript file and are given a handler to that child process. As part of the fork process, Node.js automatically creates an unnamed pipe for us so that we can talk between the two processes. Then, we listen for events on the child process and do various things based on the message that we receive.

On the child side, we can automatically listen for events from whoever spawned us and we can send messages through our process interface (this is global in each Node.js file that is started). As shown in the following code, we are able to talk between two separate processes. If we wanted to actually see this, we would have to add a timeout to our parent process so that it doesn't send the DISCONNECT message for 15 seconds, like so:

setTimeout(() => {
child.send('DISCONNECT');
}, 15000);

Now, if we bring up a task manager, we will see that two Node.js processes have been started. One of these is parent while the other one is child. We are talking over an unnamed pipe, so they are considered tightly coupled because they are the only ones that share it. This is great for systems that we want to have a parent/child relationship and do not expect to have either of them spawned in a different way.

Instead of creating this tight link between the two processes, we can use something called a named pipe (these are known as Unix domain sockets on OS X and Linux). It works similarly to an unnamed pipe, but we are able to connect two unrelated processes. To achieve this type of connection, we can utilize the net module. It provides a low-level API that can be used to create, connect, and listen to these connections. We also get a low-level socket connection, so it behaves similarly to the http(s) modules.

To start up a connection, we can do the following:

import net from 'net';
import path from 'path';
import os from 'os';

const pipeName = (os.platform() === 'win32' ?
path.join('\\\\?\\pipe', process.cwd(), 'temp') :
path.join(process.cwd(), 'temp');
const server = net.createServer().listen(pipeName);
server.on('connection', (socket) => {
console.log('a socket has joined the party!');
socket.write('DISCONNECT');
socket.on('close', () => {
console.log('socket has been closed!');
});
});

Here, we import the net, path, and os modules. The path module helps to create and resolve filesystem paths without us having to write path expressions specifically for an OS. The os module, as we saw previously, can give us information about the OS that we are currently on. When we create the pipe name, Windows needs to be at \\?\pipe\<something>. On another OS, it can just be a regular path. Something else to note is that any other OS besides Windows will not clean the pipe after we have finished using it. This means that we will need to make sure we delete the file before we exit the program.

In our case, we create a pipe name based off of the platform. In any case, we make sure that it is in our current working directory (process.cwd()) and that it is called temp. From here, we can create a server and listen for connections on this file. When someone connects, we receive a Socket object. This is a full Duplex stream, which means that we can read and write from it. We are also able to pipe information to and from it. In our case, we want to log to the console that socket joined and then send a DISCONNECT message. Once we get the close event, we just log that socket closed down.

For our client code, we should have something similar to the following:

import net from 'net';
import path from 'path';
import os from 'os';

const pipeName = (os.platform() === 'win32') ?
path.join('\\\\?\\pipe', process.cwd(), 'temp') :
path.join(process.cwd(), 'temp');
const socket = new net.Socket().connect(pipeName);
socket.on('connect', () => {
console.log('we have connected');
});
socket.on('data', (data) => {
if( data.toString('utf8') === 'DISCONNECT' ) {
socket.destroy();
}
});

This code is fairly similar, except we create a Socket object directly and try to connect to the same pipe name. Once we're connected, we log this. When we get data, we check whether it is equal to our DISCONNECT message, and if it is, we get rid of the socket.

What's nice about the IPC mechanism is that we can pass messages between different programs written in different languages. The only thing that they need to have in common is some form of common language. There are many systems out there that can do this. Although this isn't the focus of this book, note that if we needed to hook into another program, we could do this fairly easily with the net module.