Advertisement
  1. Code
  2. JavaScript
  3. Web APIs

Start Using HTML5 WebSockets With a Node.js Server

Scroll to top

One of the coolest features in HTML5 is WebSockets. WebSockets allow us to communicate with a server, without the need for traditional AJAX requests.

In this tutorial, we are going to learn a lot about WebSockets. We'll build a practical example of a client and server that communicate via WebSockets. In this tutorial, we'll be using Node.js for the server. If you want to use WebSockets with PHP, check out our companion post.

What Are WebSockets?

WebSockets are a technique for two-way communication over one (TCP) socket, a type of push technology.

Why Do We Need WebSockets?

WebSockets are widely used when an application does not favour long polling. With long polling, the server constantly sends replies that may or may not have the required data. This data is required to keep the connection fresh and alive. When a client receives the data, it checks and raises another request if needed. Of course, there are multiple benefits and use cases for long polling. But long polling is not the best technology for client-server communication. If the connection times out, a new one will be required. Also, long polling can result in very poor resource utilisation. 

These are two of the major reasons why a push technology is required. As suggested by its name, the server pushes data whenever it has something for the client. That means the client does not have to raise periodic requests. 

Let's begin our demo with the web server. This is a simple demo that will help you get started with the entire technology. 

1. Building the Node.js Server

Step 1: Choose Your Library

When it comes to Node.js, you have a dozen libraries to choose from for building the server. My favourite is ws, an extremely simple library for building servers. The library allows us to create both servers and clients. How cool is that? 

Of course, the prerequisite for using ws would be installing Node. So don't forget to download and install Node.js in your system.

Next, you need to install the library. Well, that's a very simple job where you run the following command:

1
npm install ws

Then open your most-loved code editor, and create a file called index.js.

Step 2: Create the WebSocket Server

First things first, you need to import the ws module. This is rather simple. You have to type the following line, and it will ensure that ws is imported into your application.

1
const WebSocket = require('ws')

Next, you have to create a WebSocket server on a port. The client will communicate with the server through this port. In our case, the port number is 8081.

1
const wss = new WebSocket.Server({ port: 8081 })

So what should happen when the client wants to communicate with the server? We need to key down the logic for this. The ws module comes with multiple events for each of the following changes in state:

  • open
  • close
  • message
  • connection
  • upgrade
  • ping

Also, the module comes with some functions to trigger and handle the above events:

  1. on
  2. send
  3. onerror

In our simple server, we would emit the connection event whenever the client connects. To emit one of the events, we can use the on function.

1
wss.on('connection', function connection(ws) {});

If there's an error, the onerror function can be used. Here's a brief example of how these functions can be used.

1
ws.on("close", () => { /* handle the event */ });
2
ws.onerror = function (error) { /* handle the error */ }

The basic skeleton of our WebSocket server, which opens and closes port 8081 for the client to connect with, is as below:

1
const WebSocket = require('ws')
2
3
const wss = new WebSocket.Server({ port: 8081 })
4
5
wss.on('connection', function connection(ws) {
6
    ws.on("close", () => {
7
        console.log("Client disconnected");
8
    });
9
    ws.onerror = function () {
10
        console.log("Some Error occurred");
11
    }
12
});

Step 3: Receiving a Message

A server can only perform one of these two operations with a client: send a message or receive a message. Firstly, let's see how to receive a message from a client. Once the client and server connection has been established, the server can receive a message using the message function. 

Websocket communication always happens with frames. These are data fragments that get transferred from the client to the server or vice versa. The data fragments can be in the following formats:

  • Text frames: these contain data in the form of raw text.
  • Binary data frames: these contain binary pieces of data.
  • Ping-pong frames: these are mostly used to check if the connection was established between the client and server. The browser often takes the responsibility of responding to these messages.
  • Connection close frames: these are used to handshake the connection closing.

When the browser talks to the server, which happens in our case, the text or binary frames are used. The .send() functionality of the client tends to send messages as binary or text data. This includes various formats like ArrayBuffer and Blob. You don't have to configure anything to send data in a specific format. Just send it out and decode at the server. 

Meanwhile, if you want to explicitly mention the type of frame, use the .binaryType property of the socket.

1
ws.binaryType = "arraybuffer"

If you want to decode the buffer data to a string, you can use the following function. Here we make use of Buffer.from(), followed by .toString() to convert the buffer to a string.

1
ws.on("message", (msg) => {
2
    var buf = Buffer.from(msg);
3
    console.log(buf.toString());
4
});

Step 4: Sending a Message to the Client

The next functionality of the server is to send a message to the client. For this, we make use of the ws.send() function.

Now that we have the basics of our server identified and defined, let's build the application where the server sends a message to the client whenever the client connects with it. In the below piece of code, the server continues to send 'hello world' every second, until the client disconnects. 

This is how simple it is to send a message to the client.

1
const interval = setInterval(() => {
2
    ws.send('hello world')
3
}, 1000)

Step 5: Running the Server

Finally, you need to run the server. For this, you need to open a terminal window, type the following command, and leave the server running. Don't forget to open the terminal in the folder where the index.js created in step 1 is present.

1
node index.js

Here's the entire server code to connect, disconnect, send, and receive messages:

1
const WebSocket = require('ws')
2
3
const wss = new WebSocket.Server({ port: 8081 })
4
5
wss.on('connection', function connection(ws) {
6
    console.log('Client connected')
7
    
8
    const interval = setInterval(() => {
9
        ws.send('hello world')
10
    }, 1000)
11
12
    ws.on("close", () => {
13
        console.log("Client disconnected");
14
    });
15
16
    ws.onerror = function () {
17
        console.log("Some Error occurred");
18
    }
19
    
20
    ws.on("message", (msg) => {
21
        var buf = Buffer.from(msg);
22
        console.log(buf.toString());
23
    });
24
});

2. Building the Client

Now that the server is up and running, it's time to build the client. For this, create another Node project with the following files: 

1
node_modules
2
index.html
3
index.js
4
package.json

Step 1: Install the Library

Just as we installed ws to build our server, you have to install ws in the client package as well. Your package.json should have the following dependency:

1
{
2
  "dependencies": {
3
    "ws": "^8.8.1"
4
  }
5
}

Step 2: Connecting to the Web Server

In order to connect with the web server, we have to initialise a new WebSocket. The WebSocket also needs to connect to the port defined while building the web server. In our case, it will again be 8081.

1
var hostURL = "ws://localhost:8081";
2
webSocket = new WebSocket(hostURL);

There are three important parts to the host URL:

  • scheme: ws
  • host: localhost
  • port: 8081

With new WebSocket(url), the connection happens immediately. During the process of establishing the connection, the client (aka browser) uses headers to ask the server if WebSockets are supported or not. If the server responds with a 'yes', the connection is established, and the two start talking. The protocol used is the WebSocket Protocol. Here, we don't use the HTTP Protocol at all!

Here are a few of the request headers in our client-server connection.

1
GET ws://localhost:8081/ HTTP/1.1

2
Host: localhost:8081
3
Connection: Upgrade
4
.
5
.
6
Upgrade: websocket
7
.
8
Sec-WebSocket-Version: 13
9
.
10
.
11
Sec-WebSocket-Key: cBtV+sKFkk3wqmFFr909Vg==
12
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
  • Connection: upgrade says that the client needs a switch in protocol.
  • Upgrade: websocket mentions that the protocol requested is "websocket".
  • Sec-WebSocket-Key is a browser-generated key. This is a random number that makes sure that the server supports the WebSocket protocol. It also prevents various proxies from caching any conversation made between the client and server.
  • Sec-WebSocket-Version identifies the version of the WebSocket protocol. The most recent one is 13.

If the server has agreed to establish a connection, the response would be 101, and the response headers will be as below:

1
101 Switching Protocols
2
Upgrade: websocket
3
Connection: Upgrade
4
Sec-WebSocket-Accept: abcd=

Sec-WebSocket-Accept has a key generated using a very special algorithm. The moment the browser (aka client) notices this key, it knows that the server actually supports the WebSocket protocol. Henceforth, data can be transferred in the form of frames. 

You cannot emulate WebSocket Handshakes. You cannot use fetch or XMLHttpRequest to make an HTTP request. Why? Because JavaScript does not have permission to set the headers mentioned above.

Step 3: Define the WebSocket Events

Before we define our WebSocket events, you need to have a better understanding of them. In our client, we'll use three different events:

  • onOpen: Functionality to be triggered when the socket opens.
  • onMessage: Functionality to be triggered when a message is received from the server.
  • onClose: Functionality to be triggered when the server is closed.
WebSocket EventsWebSocket EventsWebSocket Events

The above events have to be called using the WebSocket object.

1
webSocket.onopen = function(){}
2
3
webSocket.onmessage = function(msg){}
4
5
webSocket.onclose = function(){}

In our demo, the client will connect with the server and display any message received from the server on the HTML page. Likewise, any change in state from connect to disconnect will be printed to the HTML page. To achieve this, we will write a connect method, which will implement the WebSocket events accordingly.

1
function connect(){
2
    try{
3
        webSocket = new WebSocket(hostURL);
4
        messageDiv.innerHTML = "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
5
        webSocket.onopen = function(){
6
            messageDiv.innerHTML += "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
7
            connectBtn.disabled = true;
8
            disconnectBtn.disabled = false;
9
        }
10
11
        webSocket.onmessage = function(msg){
12
            messageDiv.innerHTML += "<p>Server response : " + msg.data + "</p>";
13
        }
14
15
        webSocket.onclose = function(){
16
            messageDiv.innerHTML += "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
17
            connectBtn.disabled = false;
18
            disconnectBtn.disabled = true;
19
        }
20
21
    }catch(exception){
22
        messageDiv.innerHTML += 'Exception happen, ' + exception;
23
    }
24
}

You can see that our connect function has a try/catch block. It's good coding practice to enclose all server communication within the try/catch block. If something goes wrong, we need to have a fallback strategy, and try/catch offers this to us! It gives the client a way of letting the user know that something has gone wrong with the connection. Also, it is a great place to debug the overall flow. 

In the above method, the onmessage function takes the role of printing any message from the server on the HTML page. 

Step 4: The Bridge

The connect function does not start the client-server connection immediately. We can see that the websocket is defined, but the definition of the messageDiv, connectBtn, disconnectBtn, and hostUrl seems to be coming from somewhere else. 

We have broken the client code into two sections, init() and connect(). It is the responsibility of the init function to load all these variables, and the init function has to be loaded when the page loads. The definition of the init function is given below.

1
function init(){
2
3
    messageDiv = document.getElementById("message"); 
4
    textInput = document.getElementById("text");
5
6
    hostURL = "ws://localhost:8081";
7
    websocketReadyStateArray = new Array('Connecting', 'Connected', 'Closing', 'Closed');
8
9
    connectBtn = document.getElementById('connect');
10
    disconnectBtn = document.getElementById('disconnect');
11
12
    connectBtn.disabled = false;
13
    sendTextBtn.disabled = true;
14
    sendJSONObjectBtn.disabled = true;
15
    disconnectBtn.disabled = true;
16
}

Step 5: The Client Page

The client page is rather straightforward. We are going to have the following:

  • a div where the message from the server will be loaded
  • two buttons to connect and disconnect from the server

Here is the HTML code for building this page. It needs to be entered in index.html:

1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="ISO-8859-1">
5
<title>Html5 WebSockets Example</title>
6
<script type="text/javascript" src="index.js" charset="utf-8"></script>
7
<style>
8
    .block{
9
        display:block; 
10
        margin-top: 10px;
11
    }
12
</style>
13
</head>
14
<body onload="init()">
15
<h3>Html5 WebSockets Example.</h3>
16
<div id="message" class="block"></div>
17
<button id="connect" onclick="connect()">Connect</button>
18
<button id="disconnect" onclick="disconnect()">Disconnect</button>
19
</body>
20
</html>
WebSocket Client DemoWebSocket Client DemoWebSocket Client Demo

As soon as the connection is established, the client will receive 'Hello World' from the server. Now, the page will appear as below.

Websocket client demoWebsocket client demoWebsocket client demo

The complete piece of code in the client's index.js is as follows:

1
var webSocket;
2
var messageDiv; 
3
var textInput;
4
var hostURL;
5
var websocketReadyStateArray;
6
var connectBtn;
7
var disconnectBtn;
8
9
function init(){
10
11
    messageDiv = document.getElementById("message"); 
12
    textInput = document.getElementById("text");
13
14
    hostURL = "ws://localhost:8081";
15
    websocketReadyStateArray = new Array('Connecting', 'Connected', 'Closing', 'Closed');
16
17
    connectBtn = document.getElementById('connect');
18
    disconnectBtn = document.getElementById('disconnect');
19
20
    connectBtn.disabled = false;
21
    sendTextBtn.disabled = true;
22
    sendJSONObjectBtn.disabled = true;
23
    disconnectBtn.disabled = true;
24
}
25
26
27
function connect(){
28
    try{
29
        webSocket = new WebSocket(hostURL);
30
        messageDiv.innerHTML = "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
31
        webSocket.onopen = function(){
32
            messageDiv.innerHTML += "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
33
            connectBtn.disabled = true;
34
            disconnectBtn.disabled = false;
35
        }
36
37
        webSocket.onmessage = function(msg){
38
            messageDiv.innerHTML += "<p>Server response : " + msg.data + "</p>";
39
        }
40
41
        webSocket.onclose = function(){
42
            messageDiv.innerHTML += "<p>Socket status:" + websocketReadyStateArray[webSocket.readyState] + "</p>";
43
            connectBtn.disabled = false;
44
            disconnectBtn.disabled = true;
45
        }
46
47
    }catch(exception){
48
        messageDiv.innerHTML += 'Exception happen, ' + exception;
49
    }
50
}
51
52
function selectAll(){
53
    textInput.select();
54
}
55
56
57
function disconnect(){
58
    webSocket.close();
59
}

Step 6: Sending Data From the Client to the Server

Until, the above piece of code, you will be able to send data from the server to the client. In order to send data from the client to the server, you have to implement a few more lines of code.

We are going to add a text box in our HTML page where the user can input data, and a button to send the content of the text box to the server when clicked.

1
<p class="block">Please input some text to Send : <input type="text" id="text" onfocus="selectAll()"/></p>
2
3
<!-- ... -->
4
5
<button id="sendMessage" onclick="sendMessage()">Send Message</button>
With text inputWith text inputWith text input

When the button is clicked, the following method gets invoked to send a message to the server. Since the default frame of the web socket is used, the message will be sent as a Buffer

1
function sendMessage(){
2
    webSocket.send(textInput.value)
3
}

Upon clicking Send Message, the following gets displayed in the server's console: 

client connected messageclient connected messageclient connected message

Step 7: Running the Client

To run the client, you don't have to do much. You need to open index.html in your favourite browser, and then click on Connect to establish the connection with the server.

That's it!

Conclusion

This brings us to the end of our tutorial on WebSockets using HTML and JavaScript (Node.js). WebSockets are extremely exciting and have changed drastically in the past few years. WS is just one of the many web socket libraries to choose from. You have to keep an eye on the WebSocket API to learn all about the newest changes to this push technology. 

WebSockets are a persistent way of establishing a browser-server connection. They don't have cross-origin limitation, and they're supported in all modern browsers. The API is simple, with methods like .send and .close

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.