preface

With the development of the Web, users for the Web real-time push requirements are increasingly high, before the emergence of WebSocket, in most cases is through the client to initiate polling to get real-time updates of the server data, because http1. x protocol has a defect is that communication can only be initiated by the client. The server cannot actively push to the client. This method is obviously inefficient and has poor experience in scenarios with high requirements on real-time performance, such as instant messaging and instant quotation. In order to solve this problem, the WebSocket protocol came into being, which realized the capability of two-way communication between the client and the server. Before introducing WebSocket, let’s look at the way polling implements push.

Short Polling (Polling)

The idea behind short polling is that the browser sends an HTTP request to the server every few seconds, and the server responds directly to the request, regardless of whether there is any data update. When the server response is complete, the TCP connection will be closed. The simplest code implementation is to use XHR to send a request to the back end by setInterval to get the latest data.

setInterval(function() {
  fetch(url).then((res) => {
      // success code
  })
}, 3000);
Copy the code
  • Advantages: Simple implementation.
  • Disadvantages: May cause data in a short period of time out of sync and a large number of invalid requests, poor security, waste resources.

Long Polling (long-polling)

After the client sends a request, the server will not immediately return data. The server will block the request and the connection will not be disconnected immediately. The connection will not be returned until the server has data update or the connection times out. The general effect is as follows:

The client code is as follows:

function async() { fetch(url).then((res) => { async(); // success code}).catch(() => {// timeout async(); })}Copy the code
  • Advantages: Polling has been optimized, with better timeliness.
  • Disadvantages: Keeping the connection pending consumes resources, the server does not return valid data, and the program times out.

WebSocket

The short Polling (Polling) and Long Polling (Polling) mentioned above both require the client to initiate Ajax requests before they can communicate, using the HTTP protocol, so the server cannot actively push information to the client.

When there are scenarios like sporting events, chat rooms, real-time locations, and so on, polling is inefficient and a waste of resources because you’re constantly sending requests to connect to the server. WebSocket, so that the server can take the initiative to send information to the client, so that the browser has real-time two-way communication ability.

For those of you who haven’t used WebSocket, you might think it’s some kind of advanced technology. In fact, the WebSocket API is not very common and easy to learn, but before introducing how to use it, let’s take a look at its communication principle.

Communication principle

When the client wants to establish a WebSocket connection with the server, during the handshake between the client and the server, the client will first send an HTTP request to the server, including an Upgrade request header to inform the server that the client wants to establish a WebSocket connection.

Setting up a WebSocket connection on the client side is very simple:

let ws = new WebSocket('ws://localhost:9000');
Copy the code

Like HTTP and HTTPS, WS has its counterpart, WSS, for establishing secure connections.


In the response line (General), you can see that the status code is 101 Switching Protocols, indicating that the connection has been converted from HTTP to WebSocket communication protocol. After a successful conversion, the connection is not broken, but a full-duplex communication is established, and subsequent messages are sent and received over this connection channel.

Note that there is an SEC-websocket-key field in the request header that matches the sec-websocket-Accept field in the corresponding header to provide basic protection against malicious or invalid connections. Sec-websocket-key is a base64 encoding randomly generated by the client. The server uses this encoding according to a fixed algorithm:

GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // A fixed string accept = base64(sha1(key + GUID)); // accept is the sec-websocket-accept valueCopy the code

The GUID string is a fixed string officially defined by RFC6455 and cannot be modified.

After receiving the sec-websocket-accept response from the server, the client calculates the sec-websocket-key generated by the client using the same algorithm. If a match is found, the handshake succeeds. Then determine whether the HTTP Response status code is 101 (switching protocol). If so, establish a connection and you are done.

Realize simple single person chat interface

Let’s implement a pure text message type of one-to-one chat function:

Client:

function connectWebsocket() {
    ws = new WebSocket('ws://localhost:9000'); Ws.onopen = () => {console.log()'Server WebSocket connection successful'); ws.send(JSON.stringify(msgData)); // send sends a message to the server}; Ws.onmessage = (MSG) => {let message = JSON.parse(msg.data);
        console.log('Message received:', message)
        elUl.innerHTML += `<li class="b"> small autumn:${message.content}</li>`; }; Ws.onerror = () => {console.log()'Connection failed, reconnecting... '); connectWebsocket(); }; // Listening connection closed ws.onclose = () => {console.log()'Connection closed');
    };
};
connectWebsocket();

Copy the code

As you can see above, the WebSocket instance API is easy to understand and simple to use. The send() method is used to send messages, and the onMessage event is used to receive messages, which are then processed and displayed on the page. When the onError event (listening for connection failure) is raised, it is best to perform reconnection to keep the connection alive.

Server Node: (here using the WS library)

const path = require('path');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const WebSocket = require('ws');

const wss = new WebSocket.Server({ server: server });

wss.on('connection', (ws) => {// Listen for messages sent by the client.'message', (message) => {
    console.log(wss.clients.size);
    let msgData = JSON.parse(message);   
    if (msgData.type === 'open') {// Identify session ws. SessionId = 'when initially connecting${msgData.fromUserId}-${msgData.toUserId}`;
    } else {
      let sessionId = `${msgData.toUserId}-${msgData.fromUserId}`;
      wss.clients.forEach(client => {
        if(client.sessionId === sessionId) { client.send(message); // Send a message to the corresponding client connection}})}}) // Close the connection.'close', () => {
    console.log('Connection closed');  
  });
});
Copy the code

Similarly, the server also has corresponding methods of sending and receiving. See the full sample code here

The browser and server can then happily send messages as follows:

The green arrow indicates the sent message, and the red arrow indicates the received message.

Heartbeat packet method

In the actual use of WebSocket, there may be some unstable connection when there is no message for a long time. The connection interruption caused by these unknown conditions will affect the previous communication between the client and the server.

To prove that the client and server are still alive. If the webSocket encounters network problems, the server does not trigger the onClose event. In this case, redundant connections are generated and the server continues to send messages to the client, resulting in data loss. Therefore, a mechanism was needed to detect whether the client and server were properly connected, and the heartbeat detection and reconnection mechanism came into being.

How do you detect and reconnect the heartbeat? Here, we send a data to the server at a specified interval of time (timer), the server receives the data and then sends it to the client. Under normal circumstances, the client can listen to the data returned by the server through the onMessage event, indicating that the request is normal.

If the client does not receive a response message from the server within the specified time, it determines that the connection is disconnected and closes the connection with websocket.close. The action of closing the connection can be heard through the onclose event, so we can call the Reconnect event within the onclose event to reconnect the connection.

conclusion

Through the above introduction, we should have a certain understanding of WebSocket, in fact, there is no mystery, here is a brief summary of the article content:

When a WebSocket instance is created, an HTTP request is sent.

The request message has a special field Upgrade, and the connection is converted from the HTTP protocol to the WebSocket protocol, so that the client and server establish full-duplex communication.

Information can be exchanged over this communication connection through the WebSocket send method and the onMessage event.