CORE NODEJS PART 1 TCP SOCKETS: Building A Command Line Chat App

Published Aug 29, 2017Last updated Sep 03, 2017
CORE NODEJS PART 1 TCP SOCKETS: Building A Command Line Chat App

INTRO

The most common pitfalls Nodejs/javascript developers face are libraries. My first ever introduction to Nodejs was express. Most Nodejs developers cannot really differentiate between Express server and the core Nodejs HTTP package.
This article does not cover the node HTTP package, that will be covered in later articles. Instead, it aims to

  1. Work with Node net package
  2. Read and Write to TCP Connections using sockets
  3. Build on our knowledge of the two and Implement a simple command line chatting app using sockets

NODEJS AND SOCKETS

There are three main implementations of sockets in Nodejs

  1. UPD: User Datagram Protocol
  2. TCP: Transmission Control Protocol
  3. UNIX domain

This article only covers the implementation of TCP Sockets in Nodejs.

What Is Transmission Control Protocol (TCP):

TCP is a standard that defines how network connection is established and maintained between two hosts. TCP and IP (internet protocol) defines how computer sends packets of data to each other. One of the huge difference between TCP and UDP protocol is that with TCP data integrity is guaranteed. This means if there is a loss of packets during transmission using TCP the packets are gotten back and re-sent.

Node Net Package In Action Chat App:

A direct definition from the Nodejs documentation states that the net module provides an asynchronous network API for creating stream-based TCP

There are two categories of TCP socket you can write -

  1. server
  2. client.

We'll be implementing the server side Connection and using a tool called Netcat to connect to our server.

If you use a mac, then you already have netcat installed.Windows users can follow this link to get netcat setup

const { createServer } = require('net');

const server = createServer();
server.listen(3000)

Calling the create server method from the net package creates a TCP server. This is as simple as ABC right? Calling the listen method on the server opens a connection on 3000.

If we try to connect to that port using Necat nothing happens.

> nc localhost 3000 // we get nothing

The TCP server is also an event Emitter so we can listen for client connections.

The power of the net socket is that it's a duplex stream, meaning it implements both readable and writeable, and it's also an event emitter.

Knowing this, we can listen for events like when a client connects to our TCP port and send back data to that client. Let's see this in action.

const { createServer } = require('net');

const server = createServer();

server.on('connection', (socket) => {
  socket.write("A client has connected\n")
});

server.listen(3000)

Start up your server, open a new terminal and run the line below

> nc localhost 3000  //prints A client has connected

You should see the phrase "A client has connected". If you try opening another terminal and connecting you will get the same message.

Recap:We have been able to

  1. Create a TCP server
  2. Listening for connection
  3. Connect to our server from the client
  4. Write to the client when they connect

Next, we need to figure out how to read streams from a connected client. By doing this, we can read client inputs on the server and broadcast it to every connected client.

const { createServer } = require('net');

const server = createServer();

let counter = 0;
const sockets = {}

server.on('connection', (socket) => {
  socket.id = counter++
  sockets[socket.id] = socket;
  socket.write("A client has connected\n")

  socket.on('data', data => {
    Object.entries(sockets).forEach(([key, cs]) => {
      if(sockets.id != key) {
        cs.write(data);
      }
    });
  })
});

server.listen(3000, () => {
  console.log('server has started')
})

First, we assigned an id to every socket that connects to the server and added that socket to a global object called sockets.
This global object holds all connected sockets, you can begin to imagine what we can do with this.

From line 13, you'll see how I'm listening for incoming data and looping through all the entries in the sockets object. I use Object.entries to create an array of key, value pair of all sockets in the sockets variable and then I used array destructuring to get the key and value, The value been each socket in the object.
Finally, I just write the data to all connected sockets in the global object.

To test this start the server and use nc to connect multiple clients and try sending a message.
Screen Shot 2017-08-29 at 10.26.47 AM.png

Recap:We have been able to

  1. Save connected Clients
  2. Broadcast message to all saved Clients

Finally, let's improve our chat app by giving the clients ability to input their names, so we can identify who the message is coming from.

const { createServer } = require('net');

const server = createServer();
let counter = 0;
let sockets = {}

server.on('connection', socket => {
  socket.id = counter++;
  
  console.log('client has connected')
  socket.write('Please enter your name: ')

  socket.on('data', data => {
    if(!sockets[socket.id]) {
      socket.name = data.toString().trim();
      socket.write(`Welcome ${socket.name}!\n`);
      sockets[socket.id] = socket;
      return;
    }
    Object.entries(sockets).forEach(([key, cs]) => {
    console.log(`${socket.id}: ${data}`)
      if (key != socket.id) {
        cs.write(`${socket.name}: `);
        cs.write(data);
      }
    })
  });
  socket.setEncoding('utf8');
  socket.on('end', data => {
    delete sockets[socket.id];
    Object.entries(sockets).forEach(([keys, sc]) => {
      sc.write(`${socket.name}: `);
      sc.write('has disconnected\n');
    })
    console.log(`${socket.name} has disconnected`)
  })
})

server.listen(3000, () => console.log('Server started'));

Nothing much was added to this code other than collecting the client's name when (s)he connects to the server. I checked if the socket does not exist and assign the incoming data to a name property in the user socket, and finally wrote to the connected user a welcome message with their name.

One more change was to append the name of the user sending the message to be broadcasted on line 23

Finally, I listened for when a user disconnects and broadcasted that to all client connected to the server using the socket.on('end') listener.

Summary

Although this might be a low-level implementation of sockets, It shows you the power of TCP/IP and it's underline implementation in node js.

We were able to create a TCP server and connect to it.
We also read and write streams of data to and fro multiple connected TCP sockets using events in NODEJS net.socket.

Feel free to play around with the code, and try notifying all users when a connection is made.

Discover and read more posts from Enaho Isiwele
get started
Enjoy this post?

Leave a like and comment for Enaho

1