Node.js has a ‘net’ module which provides an asynchronous network API for creating stream-based TCP/IPC servers and clients.
It can be accessed using:
const net = require('net');
To create a TCP/IPC based server, we use the createServer method.
var server = net.createServer();
The ‘server' object is of type net.Server. Let’s explore a few properties, events and methods on this class.
First and foremost, the method needed is ‘listen’ which starts the server for listening to connections in async, firing the ‘listening’ event.
server.listen(9000, () => {
console.log('opened server on port: ', 9000);
});
To find out on which address a server is running, we can use the address() method on the net.Server instance. If we need to log the port on which the server is running, then we can get this info as well without hardcoding.
server.listen(9000, () => {
console.log('opened server on %j', server.address().port);
});
The first parameter of listen is the port in which the server starts listening, and a callback which gets called once it has started listening.
A few of the common errors raised are:
- ERR_SERVER_ALREADY_LISTEN – server is already listening and hasn’t been closed.
- EADDRINUSE – another server is already listening on the given port/handle/path.
Whenever an error happens, an ‘error’ event is raised. We can hook to it and capture the errors accordingly.
server.on('error', (e) => {
if (e.code === 'EADDRINUSE') {
console.log('Address in use, retrying...');
setTimeout(() => {
server.close();
server.listen(PORT, HOST);
}, 1000);
}
});
Whenever a client connects to this server then a 'connection' event is raised and in the callback we can get hold of the client object for communicating data.
server.on("connection", (socket) => {
console.log("new client connection is made");
});
The second parameter is actually a callback which has the reference to the connection object, and the client object is of type ‘net.Socket’. To get the details like address and port, we can rely on remoteAddress, and remotePort properties respectively.
server.on("connection", (socket) => {
console.log("Client connection details - ", socket.remoteAddress + ":" + socket.remotePort);
});
Let’s assume that we are developing an application server like bot which needs to take inputs from clients and respond to the client. We can get hold of the client object and send messages to it from the server.
As soon as the client is connected, we can send a sample return message on successful connection.
server.on("connection", (socket) => {
console.log("Client connection details - ", socket.remoteAddress + ":" + socket.remotePort);
socket.write('SERVER: Hello! Connection successfully made.<br>');
});
Now if there is any data being sent by client, we can capture that data on the server by subscribing to ‘data’ event on the client socket object.
socket.on('data', (data) => {
console.log(data.toString());// since data is streamed in bytes, toString is used.
});
Some of the most commonly used events on ‘net.Socket’ are data, error and close. As the names suggest, data is for listening to any data sent, error when there is an error happens and close event is raised when a connection is closed which happens once.
Here is an example in server.js file:
const net = require('net');
var server = net.createServer();
server.on("connection", (socket) => {
console.log("new client connection is made", socket.remoteAddress + ":" + socket.remotePort);
socket.on("data", (data) => {
console.log(data.toString());
});
socket.once("close", () => {
console.log("client connection closed.");
});
socket.on("error", (err) => {
console.log("client connection got errored out.")
});
socket.write('SERVER: Hello! Connection successfully made.<br>');
});
server.on('error', (e) => {
if (e.code === 'EADDRINUSE') {
console.log('Address in use, retrying...');
setTimeout(() => {
server.close();
server.listen(PORT, HOST);
}, 1000);
}
else {
console.log("Server failed.")
}
});
server.listen(9000, () => {
console.log('opened server on %j', server.address().port);
});
‘net’ module also has another class type net.BlockList. This helps in controlling or disabling the inbound or outbound traffic based on rules from any specific IP addresses, IP ranges, or IP subnets.
Here is an example snippet from the documentation:
const blockList = new net.BlockList();
blockList.addAddress('123.123.123.123');
blockList.addRange('10.0.0.1', '10.0.0.10');
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
console.log(blockList.check('123.123.123.123')); // Prints: true
console.log(blockList.check('10.0.0.3')); // Prints: true
console.log(blockList.check('222.111.111.222')); // Prints: false
// IPv6 notation for IPv4 addresses works:
console.log(blockList.check('::ffff:7b7b:7b7b', 'ipv6')); // Prints: true
console.log(blockList.check('::ffff:123.123.123.123', 'ipv6')); // Prints: true
Now that we have the server up and running, we can build a client to connect to the server and start sending bi-directional data. This client could be another node.js application, java/c# application working with TCP sockets, asp.net MVC application talking to node.js TCP server or any other client application. But that client application should have TCP based communication mechanism support.
Since we are talking about ‘net’ module, let’s build the client application as well using net module. Moreover, it supports TCP based communication as well.
'net’ module has a factory function called ‘createConnection’ which immediately creates a socket and establishes a connection with the server running on the specified port.
Let's create another client.js file and create a connection.
const net = require('net');
const client = net.createConnection({ port: 9000 }, () => {
console.log('CLIENT: I connected to the server.');
});
The first parameter contains the details of the server. Since we are running the server locally, providing the port number would suffice for us as the host default address is localhost for TCP connections.
The second parameter is the callback called once the connection is made successfully with the server.
The returned value is of type net.Socket which we have learnt about earlier. Let’s hook to ‘data’ event and console log the information sent by the server.
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
Here we are not persisting the TCP connection and ending it once we receive a message from the server. We can subscribe to close event and handle any clean up needed.
client.on('end', () => {
console.log('CLIENT: I disconnected from the server.');
})
The output on the client terminal has to be:
CLIENT: I connected to the server.
SERVER: Hello! This is server speaking.<br>
CLIENT: I disconnected from the server.
Output on server terminal will be:
new client connection is made ::ffff:127.0.0.1:51680
CLIENT: Hello this is client!
client connection closed.
In case we want to continue the client instance till the server is alive, we can comment out the ‘client.end()’ call.
Any message in the terminal can be processed and sent to the server. For reading the text from terminal we use the readline module.
Here is a complete example:
const net = require('net');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const client = net.createConnection({ port: 9000 }, () => {
console.log('CLIENT: I connected to the server.');
client.write('CLIENT: Hello this is client!');
});
client.on('data', (data) => {
console.log(data.toString());
//client.end();
});
client.on('end', () => {
console.log('CLIENT: I disconnected from the server.');
})
rl.on('line', (input) => {
client.write(`CLIENT: ${input}`);
});
Both client and server now can communicate. When we type any text in client terminal, that is communicated to the server, and the server can respond back to the client via terminal.
Are you ready to unlock the power of coding? Join our Python for Beginners course and embark on a journey to become a coding maestro. No prior experience needed! Start your coding adventure today.
Conclusion
Websockets help in creating a full-duplex connection for sending messages from client to server and vice-versa. Some of the real-time use cases that you may be familiar with are chat apps, IoT devices and so on.
The Node.js net module helps you to create a server application easily, which can communicate to any type of client application like a web browser, mobile app, IoT device, Node.js client, or anything that knows TCP where the messaging need is bi-directional with streams.
‘net’ module can be used to communicate among child processes within a node.js server as well.