Server push technology

Server push technology

Environmental statement
Platform: windows 10
Browser: Chrome 95.0 four thousand six hundred and thirty-eight point five four
node: v10.16.0
npm: 6.9.0
node dependency:

  • express: 4.17.1
  • axios: v0.21.1
  • ws: 8.3.0
    Note: remember to use the server started by express http://127.0.0.1:port/xxx To access the front page

Feasible scheme

  • 1. Short polling
  • 2. Long polling
  • 3.websocket
  • 4.server-sent events(SSE)

1, Short polling

The front end sends requests at regular intervals.
Advantages: pure front-end code, no background configuration is required
Disadvantages: data synchronization has a delay (the delay time is related to the polling interval) and increases the back-end processing pressure

realization:

import axios from 'axios';
let intervalId = setInterval(() => {
    axios.post(url[, data[, config]]).then(res => {
        if (xxx) {
            clearInterval(intervalId);
        } else {
            /** some code */
        }
    });
});

2, Long polling

The server decides whether to respond to the request. If it is not time to respond, the server will hold the request until the data is returned.
Advantages: timely response, no need for the front end to keep sending requests
Disadvantages: maintaining the connection needs to consume network resources, and the program is easy to time out

realization:

<!-- client(front end) -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
    <title>Index page</title>
</head>
<body>
    <button onclick="sendMessage()">sendMessage</button>
    <button onclick="printMsg()">printMsg</button>
    <script>
        function sendMessage() {
            axios.post('/getInfo')
                .then(res => {
                    console.log('get res: ', res)
                });
        }

        function printMsg() {
            // Is the test program stuck
            console.log(123);
        }
    </script>
</body>
</html>

// Server (background)
const path = require('path');
const express = require('express');

let app = express();

let hostName = '127.0.0.1'
let port = 8908

// Return to home page
app.get('/index', function (req, res) {
    res.sendFile(path.join(__dirname, 'index.html'));
});

app.post('/getInfo', function(req, res) {
    console.log('The server received the request');
    // Suspend interface 5s
    setTimeout(() => {
        res.send(JSON.stringify({
            error_code: 0
        }));
    }, 5000);
});

// Start the server
app.listen(port, hostName, () => {
    console.log('Server started on ' + port);
})

3, websocket

websocket is a full duplex communication protocol based on TCP long connection communication. It belongs to the same application layer protocol as HTTP. They have similar parts, but they are two completely independent agreements.
Advantages: low performance overhead, high security (wss) and certain scalability
Disadvantages: browser support is required websocket support
compatibility:

realization:

<!-- client(front end) -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
    <title>Index page</title>
</head>
<body>
    <button onclick="sendMessage()">send message</button>
    <button onclick="closeWs()">close ws</button>
    <script>
        // WebSocket instantiation
        let ws = new WebSocket("ws://localhost:8756");
        ws.onopen = e => {
            //Successfully connected to server callback
            console.log('---Client: connected to server---', e.data);
        }

        ws.onmessage = e => {
            console.log('Received server message: ', e.data);
        }

        function sendMessage() {
            ws.send('client say: hello');
        };

        function closeWs() {
            ws && ws.close();
        }
    </script>
</body>
</html>

// Server (background)
const path = require('path');
const express = require('express');
// Ws requires npm install ws to be installed
const WebSocketServer = require('ws').Server;

let app = express();

let hostName = '127.0.0.1'
let port = 8906

// Return to home page
app.get('/index', function (req, res) {
    res.sendFile(path.join(__dirname, 'index.html'));
});

wss = new WebSocketServer({ port: 8756 });
wss.on('connection', ws => {
    console.log('---Server: client connected---');

    ws.on('message', message => {
        console.log('The server receives the message: ', message.toString());
    });

    ws.on('close', message => {
        console.log('Client closed: ', message.toString());
    });

    setInterval(() => {
        ws.send('server say');
    }, 5000);
});

// Start the server
app.listen(port, hostName, () => {
    console.log('Server started on ' + port);
})


4, Server sent events (SSE)

this segment is from server-sent_events after adding and deleting.
advantage:

  • SSE uses HTTP protocol, which is supported by existing server software. WebSocket is a stand-alone protocol.
  • SSE is lightweight and easy to use; WebSocket protocol is relatively complex.
  • SSE supports disconnection and reconnection by default, and WebSocket needs to be implemented by itself.
  • SSE is generally only used to transmit text. Binary data needs to be transmitted after encoding. WebSocket supports binary data transmission by default.
  • SSE supports custom message types.

Disadvantages: one way, which can only be sent by the server to the browser
compatibility:

SSE is similar to WebSocket in that it establishes a communication channel between the browser and the server, and then the server pushes information to the browser.

In general, WebSocket is more powerful and flexible. Because it is a full duplex channel, it can communicate in both directions; SSE is a single channel, which can only be sent by the server to the browser, because the stream information is essentially download. If the browser sends a message to the server, it becomes another HTTP request.

4.1 client API

4.1.1 EventSource object

When using SSE, the browser first generates an EventSource instance to initiate a connection to the server.

let source = new EventSource(url);

The above url can be in the same domain as the current web address or cross domain. When cross domain, you can specify the second parameter and open the withCredentials property to indicate whether to send cookies together.

let source = new EventSource(url, { withCredentials: true });

The readyState property of the EventSource instance indicates the current state of the connection. This property is read-only and can take the following values.

0: equivalent to constant eventsource Connecting indicates that the connection has not been established, or the disconnection is reconnecting.
1: Equivalent to constant eventsource Open indicates that the connection has been established and data can be accepted.
2: Equivalent to constant eventsource Closed indicates that the connection has been disconnected and will not be reconnected.

4.1. 2 basic usage
  • Event binding

    source.onxxx = function() {}
    // or
    source.addEventListener('xxx', function() {})
    
  • event

    open:
    Once the connection is established, the open event will be triggered
    message:
    When the client receives the data from the server, it will trigger the message event, and the data will be placed in event In data
    error:
    The onerror event is triggered when there is a communication error (such as a lost connection)

  • Custom event

    By default, the message event will be triggered by the data sent from the server. Developers can also define SSE events themselves.

    source.addEventListener('self-event', event => {
        let data = event.data;
    })
    

    For the implementation of the server, please see the following text.

  • method
    close:
    The close method closes the connection

4.2 server implementation

4.2. 1 data format

The SSE data sent by the server to the browser must be UTF-8 encoded text with the following header information:

Content-type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

Each message sent is composed of several messages, and each message is separated by \ n\n. Each message is internally composed of several lines, and each line is in the following format.

[field]: value\n

field can take four values:

  • data
  • event
  • id
  • retry

In addition, you can have lines beginning with colons to indicate comments. Usually, the server sends a comment every once in a while to keep the connection from being disconnected.

:This is a comment
4.2.2 data field

The data content is represented by data.

data:message\n\n

If the data is very long, it can be divided into multiple rows. The last row ends with \ n\n and the front ends with \ n

data:begin\n
data:continue\n
data:end\n\n
4.2.3 id field

The data identifier is represented by id, which is equivalent to the number of each piece of data.

id:meg1\n
data:message\n\n

The browser reads this value with the lastEventId property. Once the connection is disconnected, the browser will send an HTTP header containing a special last event ID header, and send this value back to help the server rebuild the connection. Therefore, this header information can be regarded as a synchronization mechanism.

4.2.4 event field

The event field represents the custom event type. The default is message event. The browser can listen for this event with addEventListener().

event: foo\n
data: a foo event\n\n

data: an unnamed event\n\n

event: bar\n
data: a bar event\n\n

The code above creates three pieces of information. The name of the first item is foo, which triggers the foo event of the browser; The second unnamed item indicates the default type and triggers the message event of the browser; The third is bar, which triggers the bar event of the browser.

4.2. 5. Retry field

The server can use the retry field to specify the time interval for the browser to re initiate the connection.

retry: 10000\n

Two situations will cause the browser to re initiate the connection: one is the expiration of the time interval, and the other is the connection error due to network errors and other reasons.

4.3 Node server instance

SSE requires the server to remain connected to the browser. For different server software, the resources consumed are different. Apache server, each connection is a thread. If you want to maintain a large number of connections, you are bound to consume a lot of resources. Node uses the same thread for all connections, so it consumes much less resources, but it requires that each connection cannot contain very time-consuming operations, such as IO read and write of disk.

The following is the SSE server of Node:

<!-- client(front end) -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE</title>
</head>
<body>
    <script>
        let source = new EventSource("/connSSE");

        // Listening server SEE start
        source.onopen = event => {
            console.log(`---event type:  ${event.type}---`);
			console.log('data: ', event.data);
            console.log(`------------------------\n\n`);
        }

        // Default message received
		source.onmessage = event => {
            console.log(`---event type:  ${event.type}---`);
			console.log('data: ', event.data);
            console.log(`------------------------\n\n`);
		};

        // Custom event listener 1
        source.addEventListener('projectOpen1', event => {
            console.log(`---event type:  ${event.type}---`);
			console.log('data: ', event.data);
            console.log(`------------------------\n\n`);
        });

        // Custom event listener 2
        source.addEventListener('projectOpen2', event => {
            console.log(`---event type:  ${event.type}---`);
			console.log('data: ', event.data);
            console.log(`------------------------\n\n`);
        });

        // SEE connection sending error
		source.onerror = event => {
            console.log('---link error!---');
			source.close();
		};
    </script>
</body>
</html>
// Server (background)
// Server (background)
const path = require('path');
const express = require('express');

let app = express();

let hostName = '127.0.0.1'
let port = 8908

// Return to home page
app.get('/index', function (req, res) {
    res.sendFile(path.join(__dirname, 'index.html'));
});

app.get('/connSSE', function(req, res) {
    res.header({
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache'
    });

    let timeOut = 1000;

    setTimeout(() => {
        res.write('data:' + (new Date()) + '\n\n');
    }, timeOut);
    timeOut += 1000;

    setTimeout(() => {
        res.write('event: projectOpen1\n');
        res.write('data:' + (new Date()) + '\n\n');

        res.write('event: projectOpen2\n');
        res.write('data:' + (new Date()) + '\n\n');
    }, timeOut);
    timeOut += 1000;

    setTimeout(() => {
        res.write('event: close\n');
        res.write('data: test close');
    }, timeOut);
    timeOut += 1000;
});

// Start the server
app.listen(port, hostName, () => {
    console.log('Server started on ' + port);
})

Keywords: node.js Front-end Vue.js

Added by tylerdurden on Wed, 22 Dec 2021 11:13:35 +0200