In the modern world, everything happenes so fast and it is expected from the today’s technology and devices to provide real-time experience in all fields.
To achieve that and when the old request-response mechanism just did not get the job done, developers started to come out with new or improved methods of establishing client-server communication.
There are several techniques used nowdays to achieve (almost) real time behaviour.
Some of them are:
- Polling: Short or Long
- Server-Sent Events (SSEs)
- WebSockets
Example of a typical communication between a web browser (client) and a web server using HTTP
In this article we will get familiar with Long-Polling and Server-Sent Events (SSEs), what are the advantages and disadvantages of using one or the other, and show both techniques on concrete coding examples.
Long-Polling
The Long-Polling process consists of the following steps:
- The client makes a request to the server.
- The server receives the request and delays sending anything to the client until the requested data is available or there is any other update related to data.
- When the data is available, the response is sent to the client.
- The client receives the response.
- The client usually makes a new request right away or after some defined interval so that the connection with the server is established again.
The client usually makes a new request right away or after some defined interval so that the connection with the server is established again.
NOTE: With the Short-Polling technique, the client does not send the request and wait (like in case of Long-Polling), but it constantly sends requests with some interval (for example, every 5 seconds) asking for the data. If the data is not ready yet or the server does not have any other update for the client, a lot of unnecessary responses will be sent to the client indicating that the data is not ready.
Code Example
We will show a simple chat application where messages can be received and sent. A client will open a connection to the server and wait for new messages. After the other user sends the message, it will be displayed to the client in real-time.
clientSide.js
//the client subscribes to an event comming from http://localhost:3000/read
//after receiving the response or in case of timeout, request is made again
var LongPollingFunction = {
subscribe: function (callback) {
var longPoll = function () {
$.ajax({
method: 'GET',
url: '/read',
cache: false,
success: function (data) {
callback(data)
},
complete: function () {
longPoll()
},
timeout: 30000
});
}
longPoll();
}
};
server.js
var express = require('express');
var bodyParser = require('body-parser');
//we import events module needed for emitting events
var events = require('events');
var app = express();
//we create object needed for emitting events
var eventEmitter = new events.EventEmitter();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//route used to read messages
//after the message is received it will be displayed in browser
app.get('/read', function(req, res) {
console.log("Waiting for the message to be sent.")
eventEmitter.once('message', (from, to, message) => {
res.send({from, to, message});
});
});
//route used for sending messages
//message has three information: sender, recipient and text of the message
app.post('/send', (req, res) => {
const from = req.body.from;
const to = req.body.to;
const message = req.body.message;
eventEmitter.emit('message', from, to, message);
res.status(200).end()
});
app.listen(3000);
You can start an example by executing command node server.js
from within your folder with the example files.
After starting the server and opening http://localhost:3000/read , the message in the console will be displayed indicating that you are waiting for the message to be sent to you.
You can send the message to the client by executing following command from a new terminal:
curl -X POST "http://localhost:3000/send" \
-H "Content-Type: application/json" \
-d '{"from":"Majra", "to":"Lilly", "message" : "Do not forget your meeting."}'
After the message is sent, it will be displayed to the client in the browser.
Server-Sent Events (SSEs)
SSE is a way of establishing long-term communication between client and server that enables the server to proactively push data to the client.
It is unidirectional: once the client sends the request it can only receive the responses without the ability to send new requests over the same connection.
SSE process consists of the following steps:
- The client makes a request for some data from the server.
- The connection between client and server is being established and it remains open.
- The server sends responses/events to the client every time a new data or an update about requested data is available.
Code Example
Let’s now demonstrate SSE using a very simple use case: the client will send a GET request to the route http://localhost:3000/random and it will subscribe in order to listen for events coming from the server from that route.
On the server-side, a random number will be generated and the messages will be sent to the client in intervals of 2 seconds.
The client will display messages in the web browser to the user.
If the generated number is equal to 100, the final response is sent from the server and the stream is closed.
index.html
!<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="clientSide.js"></script>
</head>
<body>
<h1>Responses from the server</h1>
<div id="randomNumberResponse"></div>
</body>
</html>
clientSide.js
//we create new EventSource object used to receive events sent from server
//parameter is URL of the web page that sends responses
var eventListener = new EventSource("http://localhost:3000/random");
//we create event listener that will listen for onmessage events
//onmessage event occurs when a message is sent from server
//in function body we specify what to do with the response: display number in browser
eventListener.onmessage = function(event) {
document.getElementById("randomNumberResponse").innerHTML += event.data;
};
server.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
var interval;
//handles the GET requests sent from client
//setInterval(function, milliseconds) calls the function in intervals specified with milliseconds
app.get("/random", function(req, res) {
res.writeHead(200, {'Content-Type': 'text/event-stream','Connection': 'keep-alive', 'Cache-Control': 'no-cache'});
interval = setInterval(writeRandomNumber, 2000, res);
});
//generates random number between 1 and 100
//message with random number is sent to the client
//if the random number is equal to 100, sent the message and close the stream
function writeRandomNumber(res) {
const randomNumber = Math.floor((Math.random() * 100) + 1);
if (randomNumber === 100) {
clearInterval(interval);
res.write("Closing the stream with the client.")
res.end();
}
else {
res.write("The random number is: " + randomNumber + '\n');
}
}
app.listen(3000);
Use cases, advantages and concerns
SSE better fits business cases that use one-way communication: reviewing some statistical information that is prone to changes frequently (stock market, web analytics and similar).
Some advantages of using SSE are:
- Simple to implement and use, both on the client and the server side.
- You can use built-in events or create custom ones.
- It is supported by most of the commonly used web browsers such as Chrome, Mozilla Firefox and Safari but it is not supported by Internet Explorer.
An example where it is not good to use SSE is some chat application where messages are sent and received constantly.
Some concerns about using SSE are:
- Unidirectional nature can cause a problem if the connection is lost. In this situation, the server may not immediately realize that the connection is lost since the client cannot notify the server about it.
- Limitation related to the number of connections that can be opened between the client and server at the same time.
On the other hand, Long-Polling is considered an older method of communication between client and server. Since there are probably a lot of devices that do not support newer methods such as SSEs and WebSockets, Long-Polling can be useful in such situations. Also, Long-Polling can be used as a fallback option.
There are a lot of concerns when using Long-Polling. Some of them are:
- Performance degradation: server needs to do more jobs such as holding the connection open, establishing what pieces of data are already sent to the client in the previous connections and what more needs to be sent. Also, a lot of time is lost in the process of setting up connections itself.
- Message ordering: there is a possibility that the same data will be written multiple times in the client’s local storage. That can happen when the client sends more than one request for the same data in parallel.
- Maximal Latency: because of the way Long-Polling works, once the server sends a response to the client, it can not send anything else and it needs to wait until a new request is made. There are some methods that can reduce latency such as HTTP pipelining, but they are not always available.
Summary
If you are wondering what is the best technique to use, there is no one correct answer that fits all the business cases.
It all depends on what you are building and trying to achieve, which techniques your infrastructure supports and are there any limitations.
That is the reason why it is important to know the main differences between Long-Polling and Server-Sent Events (and other popular available options), what are their advantages and disadvantages and based on that we can decide what fits the best for our concrete business case.