Build Custom TCP Servers with Node.js net.createServer

The net.createServer method in Node.js is a powerful, low-level tool that allows developers to build custom TCP (Transmission Control Protocol) servers. This article explains how the method works, details how to handle raw data streams through socket connections, and provides a practical implementation example to help you build your own network applications.

Understanding the net.createServer Method

The net module is a core, built-in Node.js module that provides an asynchronous network API for creating stream-based TCP servers and clients. When you call net.createServer(), Node.js initializes a new TCP server instance.

Unlike higher-level protocols like HTTP, TCP operates at the transport layer. This means net.createServer does not parse headers or manage request-response lifecycles automatically. Instead, it provides a raw, bi-directional pipe between the server and the connected client.

How the Connection Lifecycle Works

The net.createServer method accepts an optional listener callback function that automatically executes whenever a new client connects to the server.

const net = require('net');

const server = net.createServer((socket) => {
    console.log('A client has connected.');
});

The callback function receives a socket object (an instance of net.Socket). This socket is a duplex stream, meaning it can both read data from the client and write data back to the client.

To start accepting connections, you must instruct the server to listen on a specific port and host using the server.listen() method:

server.listen(8080, '127.0.0.1', () => {
    console.log('TCP server is running on port 8080');
});

Handling Data and Socket Events

Because the socket object is a standard Node.js stream, you interact with it using event listeners. The three most critical events to handle are:

Writing Data Back to the Client

To send data back to the client, you use the socket.write() method. This sends raw data across the TCP socket. You can also gracefully close the connection from the server side using the socket.end() method.

A Complete TCP Echo Server Example

The following example demonstrates a functional TCP “echo” server. When a client connects and sends text, the server logs the input and writes the exact same message back to the client.

const net = require('net');

const server = net.createServer((socket) => {
    socket.write('Welcome to the custom TCP server!\n');

    // Handle incoming data from the client
    socket.on('data', (data) => {
        const message = data.toString().trim();
        console.log(`Received: ${message}`);
        
        // Echo the message back to the client
        socket.write(`You said: ${message}\n`);
    });

    // Handle client disconnection
    socket.on('end', () => {
        console.log('Client disconnected.');
    });

    // Handle connection errors
    socket.on('error', (err) => {
        console.error(`Socket error: ${err.message}`);
    });
});

// Bind the server to port 3000
server.listen(3000, () => {
    console.log('TCP server listening on port 3000');
});

You can test this server locally by running the script and using a command-line tool like Netcat or Telnet in your terminal:

nc localhost 3000

Why Build Custom TCP Servers?

Using net.createServer is ideal for building highly optimized, low-overhead network applications that do not require the overhead of the HTTP protocol. Common use cases include: