This article briefly introduces the implementation of a small demo that uses the WebSocket protocol to implement single point push

Github:github.com/SMIELPF/web…

websocket

WebSocket protocol was born in 2008 and became an international standard in 2011. All browsers already support it. Its biggest feature is that the server can take the initiative to push information to the client, the client can also take the initiative to send information to the server, > is a real two-way equal dialogue, belongs to a server push technology.

Other features include:

  • Based on TCP protocol, the implementation of the server side is relatively easy.
  • It has good compatibility with HTTP protocol. The default ports are also 80 and 443, and the handshake phase uses HTTP protocol, so it is not easy to mask the handshake and can pass various HTTP proxy servers.
  • The data format is relatively light, with low performance overhead and high communication efficiency.
  • You can send text or binary data.
  • There are no same-origin restrictions, and clients can communicate with any server.
  • The protocol identifier is WS (or WSS if encrypted), and the server URL is the URL.

On the browser side, HTML5 has provided Websocket API, and on the server side, there are many excellent third-party libraries to provide support for Websocket, such as socket. IO, express-WS, etc., which are commonly used in Node.js. Let’s use Express-WS to implement a small demo of simple Websocket communication

Client-side implementation

We implement such a Web client

ws://{host}/ws/:name
ws://{host}/ws/Bob
ws://{host}/ws/Alice

The front-end HTML:

<! DOCTYPE html> <html> <head> <title>websocket demo</title> <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
        .row {
            margin: 1rem
        }
    </style>
  </head>
  <body>
    <div class='row'<select id='sender'>
        <option value="Bob" selected>Bob</option>
        <option value="Alice">Alice</option>
        <option value="Jack">Jack</option>
      </select>
    </div>
    <div class='row'> Receiver: <select id='receiver'>
        <option value="Bob">Bob</option>
        <option value="Alice" selected>Alice</option>
        <option value="Jack">Jack</option>
      </select>
    </div>
    <textarea id='msg' class='row' rows="10" cols="30"></textarea>
    <div class='row'>
        <button id='sendBtn'</button> </div> <h3 class='row'> Received message :</h3> <div id='conversation' class='row'></div>
    <script src="/bundle.js"></script>
  </body>
</html>
Copy the code

Front-end JS code:

var sender = document.getElementById('sender');
var receiver = document.getElementById('receiver');
var conversation = document.getElementById('conversation');
var sendBtn = document.getElementById('sendBtn');
var socket = null;
var createSocket = function() {
    if(socket) {
        socket.close();
    }
    var url = 'ws://' + window.location.host + '/ws/' + sender.options[sender.selectedIndex].value;
    socket = new WebSocket(url);
    socket.onopen = function() {
        console.log('connected to ' + url);
    }
    socket.onmessage = function(event) {
        var data = JSON.parse(event.data);
        conversation.innerHTML = conversation.innerHTML + data.from + ':' + data.content + '<br/>'; 
    }
    socket.onclose = function() {
        console.log('close connect to'+ url); }}; var sendMessage =function() {
    var msg = document.getElementById('msg').value;
    fetch('/rest/message', {
        method: 'POST',
        headers: {
            'Content-type': 'application/json'
        },
        body: JSON.stringify({
            from: sender.options[sender.selectedIndex].value,
            content: msg,
            to: receiver.options[receiver.selectedIndex].value
        }) 
    }).then(res => {
        return res.json();
    }).then(data => {
        if(! data.succeed) { alert(data.msg); }})}; sender.onchange =function() {
    createSocket();
}

sendBtn.onclick = function() {
    sendMessage();
}

createSocket();
Copy the code

Server-side implementation

The server side relies on Express and Express-WS to implement two interfaces, one is websocket interface and the other is HTTP interface

The webSocket interface is implemented as follows:

const app = new express();
expressWs(app);

const wsClients = {}
app.wsClients = wsClients;

app.ws('/ws/:wid',  (ws, req) => {
    if(! WsClients [req.params.wid]) {wsClients[req.params.wid] = []} wsClients[req.params.wid].push(ws); WsClients [req.params.wid] = wsClients[req.params.wid].filter((client) => {wsClients[req.params.wid] => {wsClients[req.returnclient ! == ws; });if(wsClients[req.params.wid].length === 0) { delete wsClients[req.params.wid]; }}});Copy the code

We start by declaring a connection pool, wsClients, which is an object with a key as the name of the message sender and a value as an array that holds all corresponding WebSocket connection instances. When a WebSocket connection is established, we log the connection to the connection pool and declare a callback to clean up the connection pool when the connection is closed in the onClose method. The HTTP interface is implemented as follows:

app.post('/rest/message', (req, res) => { const to = req.body.to; // Receiver id const from = req.body.from; // send id const result = {succeed:true };
    if(wsClients[to] ! == undefined) { wsClients[to].forEach((client) => { client.send(JSON.stringify({ from, content: req.body.content })); }); }else{// If the receiver of the message is not connected, error result.succeed = is returnedfalse;
        result.msg = 'The other party is not online';
    }
    res.json(result);
});
Copy the code

The sender and receiver of the message are retrieved from the BODY of the HTTP request, and the webSocket connection instances of all the message recipients are traversed from the connection pool to push the message to the client

The complete server code is as follows. On the basis of the basic function, it periodically prints the number of Websocket connections in the connection pool:

const express = require('express');
const expressWs = require('express-ws');

const app = new express();
expressWs(app);

const wsClients = {}
app.wsClients = wsClients;
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static('./static'));

app.ws('/ws/:wid',  (ws, req) => {
    if(! WsClients [req.params.wid]) {wsClients[req.params.wid] = []} wsClients[req.params.wid].push(ws); WsClients [req.params.wid] = wsClients[req.params.wid].filter((client) => {wsClients[req.params.wid] => {wsClients[req.returnclient ! == ws; });if(wsClients[req.params.wid].length === 0) { delete wsClients[req.params.wid]; }}}); app.post('/rest/message', (req, res) => { const to = req.body.to; // Receiver id const from = req.body.from; // send id const result = {succeed:true };
    if(wsClients[to] ! == undefined) { wsClients[to].forEach((client) => { client.send(JSON.stringify({ from, content: req.body.content })); }); }else{// If the receiver of the message is not connected, error result.succeed = is returnedfalse;
        result.msg = 'The other party is not online';
    }
    res.json(result);
});

setInterval(() => {// Periodically prints the number of connection pools console.log('websocket connection counts:')
    Object.keys(wsClients).forEach(key => {
        console.log(key, ':', wsClients[key].length);
    })
    console.log('-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --');
}, 5000);

app.listen(3000, () => {
    console.log('visit http://localhost:3000');
    // child_process.execSync('start http://localhost:3000');
});
Copy the code

thinking

Now we have a simple demo of webSocket communication, but this implementation uses WebSocket to push messages to the client during HTTP POST requests, which is fine if the server is deployed on a single node

About a websocket multi – node distributed problem front end questions

Github: github.com/SMIELPF/web…

If you feel helpful, please click “like” and click “star”.