Breaching Boundaries with WebSockets: A Simple Dive into Real-Time Communication in JavaScript
Tags: javascript, webdev, beginners, tutorial
Published: 2023-09-28 12:24:18

In the digital realm, timely and seamless communication is pivotal for an enhanced user experience. One technology that stands out in facilitating real-time communication is WebSockets. Unlike traditional HTTP, where communication is initiated only by the client, WebSockets offer a two-way communication channel between the client and server, making real-time updates a breeze.

Let’s embark on a simple journey to understand WebSockets and how they are implemented in JavaScript, along with practical examples. Additionally, we'll discern scenarios where they prove to be a better choice.

### Understanding WebSockets

WebSockets provide a unique channel of communication where both the client and server can send data independently at any time. This is a significant upgrade from the conventional request-response model where the communication is always initiated by the client.

### Implementing WebSockets in JavaScript

Let’s create a simple WebSocket connection in JavaScript. Firstly, ensure your server supports WebSocket connections.

```javascript
// Creating a new WebSocket instance
const socket = new WebSocket('ws://your-websocket-server.com');

// Connection opened
socket.addEventListener('open', (event) => {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', (event) => {
    console.log('Message from server:', event.data);
});

// Connection closed
socket.addEventListener('close', (event) => {
    console.log('Server connection closed:', event.data);
});
```

In this snippet:

1. We create a new `WebSocket` instance, passing the WebSocket server URL.
2. We attach an event listener for the `open` event, which triggers when the connection is established, sending a greeting to the server.
3. We listen for any messages from the server with the `message` event.
4. Finally, we have a listener for the `close` event, which triggers when the connection is closed.

### When to Use WebSockets?

WebSockets are particularly useful when you have a need for real-time updates. Here are some use cases:

1. **Chat Applications**: Real-time message delivery is crucial for chat applications, making WebSockets a perfect fit.
2. **Live Updates**: Whether it's a stock market application or a live score update of a game, WebSockets provide the real-time data transmission required.
3. **Online Gaming**: Multiplayer online games benefit from the low latency and real-time updates provided by WebSockets.
4. **Collaboration Tools**: Tools like document editors or project boards, where multiple users collaborate in real-time, also benefit significantly from WebSockets.

WebSockets bridge the gap between client and server, making real-time communication not just a possibility, but a robust, efficient reality. As you delve into projects that require a seamless flow of data, harnessing the power of WebSockets in JavaScript will undoubtedly prove to be a game-changer.

### Websocket server with NodeJs and express

Creating a WebSocket server alongside an Express application requires the `ws` library, which is a popular WebSocket library for Node.js. Below is a basic example of how you can set up a WebSocket server using Node.js and Express.

1. Firstly, you'll need to install `express` and `ws` using npm:

```bash
npm install express ws
```

2. Now create a file named `server.js` and paste the following code into it:

```javascript
const express = require('express');
const http = require('http');
const WebSocket = require('ws');

const app = express();

// Initialize a simple http server
const server = http.createServer(app);

// Initialize the WebSocket server instance
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws) => {
    // Connection is up, let's add a simple event
    ws.on('message', (message) => {
        console.log('Received:', message);
        
        // Broadcast the message to all connected clients
        wss.clients.forEach(client => {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(`Broadcast: ${message}`);
            }
        });
    });

    // Send a welcome message to the new connection
    ws.send('Welcome to the WebSocket server!');
});

// Start the server on port 8080
server.listen(8080, () => {
    console.log('Server is listening on port 8080');
});
```

In this code:

- We create an Express app and an HTTP server.
- We initialize a new WebSocket server instance, binding it to the HTTP server.
- We listen for new WebSocket connections on the `connection` event. For each new connection, we:
  - Set up a listener for `message` events, logging the received message to the console and broadcasting it to all other connected clients.
  - Send a welcome message to the newly connected client.

Now, to start your server, run the following command in the terminal:

```bash
node server.js
```

Your WebSocket server will now be running on `http://localhost:8080`. You can connect to it using the WebSocket client code from the previous section of the article, just replace `'ws://your-websocket-server.com'` with `'ws://localhost:8080'`.

Certainly! You can create a WebSocket server using just the `ws` library and Node.js's built-in `http` module. Below is how you could go about this:

1. As before, ensure you have the `ws` library installed via npm:

```bash
npm install ws
```

2. Create a file named `server.js` and paste the following code into it:

```javascript
const http = require('http');
const WebSocket = require('ws');

// Create an HTTP server
const server = http.createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
});

// Initialize the WebSocket server instance
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws) => {
    // Connection is established
    ws.on('message', (message) => {
        // Log the received message
        console.log('Received:', message);
        
        // Broadcast the message to all connected clients
        wss.clients.forEach(client => {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(`Broadcast: ${message}`);
            }
        });
    });
    
    // Send a welcome message to the new connection
    ws.send('Welcome to the WebSocket server!');
});

// Start the server on port 8080
server.listen(8080, () => {
    console.log('Server is listening on port 8080');
});
```

In this code:

- We create an HTTP server using Node.js's built-in `http` module.
- We initialize a new WebSocket server instance, binding it to the HTTP server.
- We listen for new WebSocket connections on the `connection` event. For each new connection, we:
  - Set up a listener for `message` events, logging the received message to the console and broadcasting it to all other connected clients.
  - Send a welcome message to the newly connected client.

To start your server, run the following command in the terminal:

```bash
node server.js
```

Your WebSocket server will now be running on `http://localhost:8080`. You can connect to it using the WebSocket client code from your earlier request, just replace `'ws://your-websocket-server.com'` with `'ws://localhost:8080'`.

### Creating Chat Room

First let's create a client's UI (a html page)

index.html

```xml
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebSocket client</title>
  </head>
  <body>
    <button id="sendmessagebutton">Broadcast Message</button>
    <button id="sendtoserver">Send Message to server</button>
    <p id="logging">Logging:</p>
    <script src="client.js"></script>
  </body>
</html>

```

- The "Broadcast Message" button is for sending a message for broadcasting
- The "Send message to server" is for sending message only to server
- The paragraph is for logging things on the client

Now let's change a little bit the client.js

```javascript
// Creating a new WebSocket instance
const socket = new WebSocket('ws://localhost:8080');

// Connection opened
socket.addEventListener('open', (event) => {
    var sayhi = {
        "type": "SENDTOSERVER",
        "payload": {
            "message": "Hello server, How are you?"
        }
    }
    socket.send(JSON.stringify(sayhi));
    document.getElementById("logging").innerText += "\n" + "You sent this mesage to the server: " + sayhi.payload.message

    document.getElementById("sendmessagebutton").addEventListener("click", () => {
        var newmessage = {
            "type": "BROADCAST",
            "payload": {
                "author": "A USER",
                "message": "Hi everyone!"
            }
        }
        socket.send(JSON.stringify(newmessage))
        document.getElementById("logging").innerText += "\n" + "You sent this mesage for broadcasting to the server: " + newmessage.payload.message
    })
    document.getElementById("sendtoserver").addEventListener("click", () => {
        var newmessage = {
            "type": "SENDTOSERVER",
            "payload": {
                "author": "A USER",
                "message": "This message is for server only"
            }
        }
        socket.send(JSON.stringify(newmessage))
    })
});

// Listen for messages
socket.addEventListener('message', (event) => {
    try {
        var message = JSON.parse(event.data);
        if (message.type === 'BROADCAST') {
            document.getElementById("logging").innerText += "\n" + "A client broadcasted: " + message.payload.message
        } else {
            document.getElementById("logging").innerText += "\n" + "You received this message: " + message.payload.message
        }

    } catch (e) {
        console.log('Wrong format');
        return;
    }
});

// Connection closed
socket.addEventListener('close', (event) => {
    console.log('Server connection closed:', event.data);
});
```

We will change the server so it can differentiate the messages that are for broadcasting and for server only.

```javascript
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const path = require('path');
const app = express();

app.set("public", path.join(__dirname, "/"));
app.use(express.static(app.get("public")));
// Initialize a simple http server
const server = http.createServer(app);

// Initialize the WebSocket server instance
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws) => {
    // Connection is up, let's add a simple event
    ws.on('message', (data) => {
        console.log('Received:', data.toString());
        let message;
        try {
            message = JSON.parse(data);

        } catch (e) {
            console.log('Wrong format');
            return;
        }

        if (message.type === 'BROADCAST') {
            // Broadcast the message to all connected clients
            wss.clients.forEach(client => {
                if (client !== ws && client.readyState === WebSocket.OPEN) {
                    var newmessage = {
                        "type": "BROADCAST",
                        "payload": {
                            "author": "Server",
                            "message": message.payload.message
                        }
                    }
                    client.send(JSON.stringify(newmessage));
                }
            });
        }
        if (message.type === 'SENDTOSERVER') {
            var respond = {
                "type": "SERVER_MESSAGE",
                "payload": {
                    "author": "Server",
                    "message": "I received your message:<<" + message.payload.message + ">>"
                }
            }
            ws.send(JSON.stringify(respond));
        }
    });

    // Send a welcome message to the new connection
    var message = { type: "SERVER_MESSAGE", payload: { message: "Hello client You just connected!" } }
    ws.send(JSON.stringify(message));

});

// sendFile will go here
app.get('/', function (req, res) {
    res.sendFile(path.join(__dirname, '/index.html'));
});

// Start the server on port 8080
server.listen(8080, () => {
    console.log('Server is listening on port 8080');
});
```

Let's test things:

run `node server.js`

Then visit http://localhost:8080/

You should see this:

![websocket](https://eneaslari.com/68160236-8f0b-490a-879d-b4b578b7dc0d.PNG "1")

If you open a second tab you will connect a second client:

![websocket](https://eneaslari.com/75614a1c-6f5b-47df-b56b-f8d64b7329ad.PNG "1")

Then you can broadcast messages(press "Broadcast Message")
The server will receive the message and will broadcast it to the clients (except sender) 

![websocket](https://eneaslari.com/954dfa58-6cb3-4eb5-ab20-a18cb87f57dd.PNG "1")

Also, you can send messages only to the server and get a response

![websocket](https://eneaslari.com/4fa86f3a-35d5-428a-b67e-f86dba9994a0.PNG "1")

That's for now!

You can find the code of this article here : [GitHub Repository](https://github.com/tzagiollari/websockets-a-simple-dive-into-real-time-communication-in-javascript "gihub")

Thank you for your time and for reading!