There have already been many articles on this topic, but far from the whole truth has been revealed. For those who missed it, read
Building Real-Time Applications with Server-Sent Events .
How does Server-Sent-Events work?
A simple example:
(new EventSource('/events')).addEventListener('message', function (e) {
document.getElementById('body').innerHTML += e.data + '
';
}, false);
The browser establishes an http connection, and for each message from the server, an event is triggered, in the handler of which we can receive the message text. At the same time, it is not necessary to break the connection from the server side, as required by XMLHTTPRequest. Therefore,
once a connection is established, we can receive messages from the server.
Another advantage of Server-sent-events is that the latest specification is now supported by Chrome browsers, Opera 11+, support for Firefox Aurora is declared, and for browsers IE8 +, Firefox 3.5+ you can implement javascript polyfill, for older browsers it can use long-polling. Thus, EventSource is better supported than WebSockets. (See native support here -
http://caniuse.com/#search=eventsource )
')
At the same time, for the operation of a polyfil from the server side, it is not necessary to connect any libraries, it is enough to implement work with Server-sent-events, as required by the standard, and not to write code for every ajax transport (as is done in socket.io, which greatly complicates it, more transports - more subtleties)
And so, IE8 supports
XDomainRequest , which can work using server-push technology, i.e. we can also receive messages without interrupting http connections.
Unfortunately, XDomainRequest requires padding -
2 kilobytes at the beginning of the response body, but this is not a problem. Also, XDomainRequest does not support the setting of request headers, so the
Last-Event-ID will have to be transmitted in the body of the request (POST).
Read more about XDomainRequest and its capabilities to read here -
http://blogs.msdn.com/b/ieinternals/archive/2010/04/06/comet-streaming-in-internet-explorer-with-xmlhttprequest-and-xdomainrequest.aspx ? PageIndex = 1 )
The Firefox browser allows you to process messages from the server as they arrive through standard XMLHttpRequest (see
javascript.ru/ajax/comet/xmlhttprequest-interactive article)
Therefore, it also supports server-push.
For the rest of the browsers, you can organize the receipt of messages using the long-polling scheme using XMLHttpRequest (that is, we need to disconnect the connection from the server after each sent message).
So, ready EventSource for browsers that do not have native support -
https://github.com/Yaffle/EventSource .
Library dependencies - no, all you need to work:
<script type="text/javascript" src="eventsource.js"></script>
Problems using server-push and long-polling
Browsers limit the number of simultaneous connections, which can cause problems when opening a site in several browser tabs. Of course, in modern browsers the number of connections is limited to at least six, but if each tab with your site uses 2 or more http connections, this limit is quickly reached.
To solve this problem, SharedWorker fits best, by creating a SharedWorker, we can create an EventSource inside it and send all the events from the EventSource object to all connected scripts:
Example, sharedworker.js:
var es = new self.EventSource('events'),
history = [];
es.addEventListener('message', function (e) {
history.push(e.data);
}, false);
self.onconnect = function onConnect(event) {
var port = event.ports[0]; //
history.forEach(function (data) {
port.postMessage(data);
});
es.addEventListener('message', function (e) {
port.postMessage(e.data);
}, false);
}
But, unfortunately, SharedWorker is supported only by Opera 10.6+ and Chrome (Safari) browsers. At the same time, Opera does not have an EventSource object “inside” the SharedWorker (i.e. in the SharedWorkerGlobalScope). Therefore, this method of avoiding multiple connections is applicable only for Webkit browsers. For all other browsers we will have to create a separate domain. Because EventSource does not support cross-domain requests, I propose to connect via an iframe html-page located on the domain for the EventSource, which will “run” the EventSource and transmit messages through the “postMessage” (recently there was an article on this topic -
http: // habrahabr. com / blogs / javascript / 120336 / , which describes the use of the easyxdm library, but “window.postMessage” is supported quite well by modern browsers (
http://caniuse.com/#search=postmessage ).
Last-Event-ID
When the connection to the server is broken (either the server has closed the connection, or some network error has occurred), the EventSource reconnects after a certain time (this time can be monitored). At the same time, the new connection will contain the Last-Event-ID header — the identifier of the last message received.
Consider an example server response - event stream:
retry: 1000 \ n
id: 123
data: hello world \ n \ nThe `
retry` field indicates to the server after which number of milliseconds to reconnect if the connection is broken. The `
id` field will be transmitted in the Last-Event-ID header during reconnection. Therefore, from the server side, we can easily determine the identifier of the last message received by the user and transfer everything accumulated after it. The user will receive all messages without missing anything. For simplicity in the example, the message is sent using the GET method without authentication and protection from CSRF. Time in chat - local for the server, sorry.
Sample chat:
http://hostel6.ru:8002
Chat sources can be downloaded here:
https://github.com/Yaffle/EventSourceThank you all for your attention!
UPD: Added CORS support for IE8 +, FF4-5