Sitemap
Shruti Maharjan

Shruti Maharjan

Building a Terminal Chat Application Using TCP Sockets in Node.js

4 min readJul 14, 2026

--

In this article, we’ll build a simple terminal-based chat application using TCP sockets and Node.js. Instead of using libraries like Socket.IO, we’ll use Node’s built-in net module to understand how client-server communication works under the hood.

What is TCP?

TCP (Transmission Control Protocol) is a connection-oriented protocol that allows two devices to exchange data reliably over a network. Before data is sent, a connection is established between the client and the server.

In this project, we’ll build a chat application where multiple terminal clients can communicate with each other through a TCP server.

Project Setup

Create a new folder and add two files:

client.js
server.js

Initialize a Node.js project:

npm init -y

No external packages are required for this project since we’ll only use Node’s built-in modules.

Creating the Server

Import the built-in net module.

const net = require("net");

Create a TCP server.

const server = net.createServer();

At this point, the server exists but isn’t listening for any connections yet.

Start the server on port 3000.

server.listen(3000, () => {
console.log("Server is listening on port 3000");
});

A port is used to identify different applications running on the same machine. Here, our chat server listens on port 3000.

Handling Client Connections

Whenever a client connects, Node creates a new socket for that client.

A socket represents the connection between the client and the server. It keeps track of information such as the connection status and is used to send and receive data.

const net = require("net");
const server = net.createServer((socket) => {
console.log("Client connected");
});
server.listen(3000, () => {
console.log("Server is listening on port 3000");
});

Creating the Client

Now let’s connect to our server.

const net = require("net");
const client = net.createConnection({
host: "localhost",
port: 3000,
});

localhost refers to your own computer, and 3000 is the port where our server is listening.

Run the server first:

node server.js

Then open another terminal and run:

node client.js

You won’t see anything in the client terminal yet, but the server should print:

Client connected

Listening for Events

Let’s know when the client successfully connects.

client.on("connect", () => {
console.log("Connected to the server!");
});

on() registers an event listener. Whenever the "connect" event occurs, the callback function is executed.

Similarly, we can listen for incoming data.

client.on("data", (data) => {
console.log(data.toString());
});

Notice the use of toString(). Data received over TCP arrives as a Buffer, so we convert it into a readable string.

Sending Data from the Server

Let’s send a welcome message whenever a client connects.

const net = require("net");
const server = net.createServer((socket) => {
console.log("Client connected");
socket.write("Hello from server!\n");
});
server.listen(3000, () => {
console.log("Server is listening on port 3000");
});

Since the client is listening for the "data" event, this message will automatically appear in the client terminal.

Making the Chat Interactive

We now need a way for users to type messages in the terminal.

Node provides another built-in module called readline.

const readline = require("readline");

Create a readline interface.

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

process.stdin represents the keyboard input, while process.stdout represents the terminal output.

Read user input.

rl.on("line", (input) => {
console.log(input);
});

Every time the user presses Enter, the "line" event is triggered.

Get Shruti Maharjan’s stories in your inbox

Join Medium for free to get updates from this writer.

Instead of printing the input, send it to the server.

rl.on("line", (input) => {
client.write(input);
});

Receiving Messages on the Server

The server also needs to listen for incoming messages.

socket.on("data", (data) => {
console.log(`Received: ${data.toString()}`);
});

Now anything typed in the client terminal appears in the server terminal.

Supporting Multiple Clients

To broadcast messages, the server needs to remember every connected client.

Create an array.

const clients = [];

Whenever a client connects:

clients.push(socket);

Now the server knows every connected client.

Broadcast messages to all clients.

socket.on("data", (data) => {
const message = data.toString();
    clients.forEach((client) => {
client.write(message);
});
});

Now if one client sends a message, every connected client receives it.

Prevent Sending Messages Back to the Sender

Currently, the sender also receives their own message.

We can avoid this with a simple condition.

clients.forEach((client) => {
if (client !== socket) {
client.write(message);
}
});

Handling Client Disconnections

When a client disconnects, we should remove it from our array.

socket.on("close", () => {
const index = clients.indexOf(socket);
    if (index !== -1) {
clients.splice(index, 1);
}
});

It’s also good practice to handle socket errors.

socket.on("error", (err) => {
console.log("Socket error:", err.code);
});

Adding Usernames

Instead of anonymous messages, let’s allow each client to choose a username.

When the client connects:

rl.question("Enter your username: ", (username) => {
client.write(`USERNAME:${username}`);
});

On the server:

if (message.startsWith("USERNAME:")) {
socket.username = message.substring(9);
    console.log(`${socket.username} joined the chat`);    return;
}

The username is stored as a property on the client’s socket.

Now when broadcasting messages:

client.write(`${socket.username}: ${message}`);

Messages become much more readable.

Shruti: Hello!
Bob: Hi!

Join and Leave Notifications

When someone disconnects:

socket.on("close", () => {
console.log(`${socket.username} left the chat`);
    clients.forEach((client) => {
if (client !== socket) {
client.write(`${socket.username} left the chat\n`);
}
});
const index = clients.indexOf(socket); if (index !== -1) {
clients.splice(index, 1);
}
});

On the client:

client.on("close", () => {
console.log("Disconnected from server");
process.exit(0);
});

Final Thoughts

This project demonstrates the basics of TCP socket programming in Node.js without relying on higher-level libraries.

While libraries like Socket.IO make real-time communication much easier, building a chat application using the net module helped me better understand:

  • TCP connections
  • Client-server architecture
  • Event-driven programming
  • Socket communication
  • Managing multiple clients
  • Broadcasting messages
  • Connection lifecycle

You can find the complete source code here:

https://github.com/shruti-1007/tcp_terminal_chat

--

--