📜 ⬆️ ⬇️

Real online, offline events

With the advent of online, offline events, many developers, especially mobile web services, have placed great hopes on them. It would seem online, offline tell us when the user has access to the Internet, but in reality this is far from the case. Details of their behavior once described Rezig in his blog .



Briefly - online, offline signals to us that the user manually switched to offline or he has no connection to the network. In fact, these 2 events are useless in the form in which they are presented - I do not know who will manually switch the tab to online / offline mode, and with network connections everything is also bad. And, of course, prehistoric browsers do not know these events.
')
Under the cut, an elegant and 100% cross-browser solution that allows you to get real online, offline events.

We need to get events that check the user’s Internet presence, or rather the connection to our web service, at no extra cost.

Algorithm (I called it LIP - Long Ping Image):
1. Create a picture via new Image
2. We hang on it onload onerror
3. Register the path to the long polling resource of our ping server
4. The browser establishes a hanging connection to the server, if the connection was dropped for any reason - the Internet has dropped, http 50x, then the onerror event will fire. Here we create another image, this time “fast”, to make sure 100% that the service is offline. If this picture has worked onerror means the service is exactly offline. At a certain interval we are trying to raise the picture ping connection.

The solution is absolutely cross-browser and cross-domain. Response to offline 2-4 seconds, response to online 0-15 seconds.

Problems that were noticed:
1. Opera as a Christmas tree to everyone than it can treacherously signals to the user that “Loading ...” during a long picture connection is impossible to fix (tried the iframe, css url, sse) and the eternal “Loading ...” is annoying. For the Opera, a long connection is not established, but simply at a certain interval a “quick” picture is polled - not so quickly, it is a little expensive, but nothing can be done.
2. FF users can kill a long connection by pressing Esc when the page loads - preventDefault was fixed.
3. When you physically disconnect the Internet (pulled the cord), all browsers do not drop the hanging connection, so offline does not come.

Advantages:
1. Coverage of all browsers, even the most ancient
2. Fast response to offline / online
3. You can tweak nginx to perform the function of the ping-server and get meager costs on the server side.

Disadvantages:
1. A small leakage of traffic of about 1Kb per minute (with the ability to reduce costs to 1kb per session)
2. The need to raise the ping server
3. A large number of hanging connections
4. If the ping server is on the same domain as the main server, then we occupy 1 of 4 possible http connections.

Code


All this can be scrolled through, the archive with source codes and an example below.

ping.php - our ping server
<?php isset($_GET['long']) && sleep(55); header("Content-type: image/gif"); header("Expires: Wed, 11 Nov 1998 11:11:11 GMT"); header("Cache-Control: no-cache"); header("Cache-Control: must-revalidate"); // 1x1 gif printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c" . "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 71,73,70,56,57,97,1,0,1,0,128,255,0,192,192,192,0,0,0,33, 249,4,1,0,0,0,0,44,0,0,0,0,1,0,1,0,0,2,2,68,1,0,59); ?> 


There is another option via nginx HttpEmptyGifModule
 location = /_.gif { empty_gif; } 

I have no idea how to implement a long request for nginx, so I attached a variant on PCP.

Ping.js
 /** * @fileOveriew Long Polling Image Ping */ (function (window, Image) { /** * Long Polling Image Ping * * Way to detect user's inernet connection */ var Lpip = { BASE_URL: 'ping.php', /** * Long polling request URL. Can be crossdomain */ LONG_POLLING_IMAGE_URL: '?long', /** * Common image request. Can be crossdomain */ IMAGE_URL: '', RECONNECT_TIMEOUT: 15000, _image: null, _stage: 2, _makeImage: function (url) { var image = new Image(); image.onload = Lpip.onImageLoad; image.onerror = Lpip.onImageError; image.src = url + (url.match(/\?/) ? '&' : '?') + Math.random(); return Lpip._image = image; }, /** * @type Boolean */ online: false, /** * @type Boolean */ offline: false, /** * Long polling image request */ watch: function () { // Opera fix if (window.opera) { window.setTimeout(function () { Lpip._makeImage(Lpip.BASE_URL + Lpip.IMAGE_URL); }, Lpip.RECONNECT_TIMEOUT); return; } // For other browsers Lpip._makeImage(Lpip.BASE_URL + Lpip.LONG_POLLING_IMAGE_URL); }, /** * Quick image request */ quick: function () { Lpip._makeImage(Lpip.BASE_URL + Lpip.IMAGE_URL); }, onImageLoad: function () { if (!this.width) { // Error Lpip.onImageError.call(this); } else { // Image ok if (Lpip._stage > 1) { // Internet connection up! Lpip.onConnectionUp(); Lpip.offline = !(Lpip.online = true); } // Reset errors Lpip._stage = 0; // Continue requesting Lpip.watch(); } }, onImageError: function () { Lpip._stage += 1; if (Lpip._stage > 1) { if (Lpip._stage === 2) { // User's internet connection down... Lpip.onConnectionDown(); Lpip.offline = !(Lpip.online = false); } // Try reconnect window.setTimeout(Lpip.quick, Lpip.RECONNECT_TIMEOUT); } else { // Maybe long polling request aborts for some resons // Try to get "quick image" Lpip.quick(); } }, onConnectionUp: function () { window.console && window.console.log('onConnectionUp'); }, onConnectionDown: function () { window.console && window.console.log('onConnectionDown'); } }; // Exporting Lpip window.Ping = { /** * Inits Ping * * @param {Object} [options] * @param {Number} [options.reconnectTimeout=15000] * @param {String} [options.baseUrl='ping.php'] */ init: function (options) { Lpip.RECONNECT_TIMEOUT = options.reconnectTimeout || Lpip.RECONNECT_TIMEOUT; Lpip.BASE_URL = options.baseUrl || Lpip.BASE_URL; // User can cancel long polling request by pressing Esc button in Firefox or Opera if (window.addEventListener) { window.document.addEventListener('keypress', function (event) { if (event.keyCode === 27) { event.preventDefault(); } }, false); } Lpip.quick(); }, /** * Connection up event helper * * Supports only one listener * * @param {Function} callback */ connectionUp: function (callback) { Lpip.onConnectionUp = callback; }, /** * Connection up event helper * * Supports only one listener * * @param {Function} callback */ connectionDown: function (callback) { Lpip.onConnectionDown = callback; }, /** * @returns {Boolean} */ isOnline: function () { return Lpip.online; }, /** * @returns {Boolean} */ isOffline: function () { return Lpip.offline; } }; }(this, this.Image)); 


Usage example
 <body> <button onclick="checkConnectionStatus()">Connection Status</button> <pre id="log"></pre> <script type="text/javascript" src="Ping.js"></script> <script type="text/javascript"> (function (window, Ping, log) { log.innerHTML += 'Lpip is watching...<br/>'; Ping.connectionUp(function () { log.innerHTML += 'Connection Up<br/>'; }); Ping.connectionDown(function () { log.innerHTML += 'Connection Down<br/>'; }); // call on window.onload to prevent "loading... status" window.onload = function () { Ping.init({ 'baseUrl': '/lpip/ping.php', 'reconnectTimeout': 15000 }); }; window.checkConnectionStatus = function () { window.alert(Ping.isOnline() ? 'Online' : 'Offline'); } }(this, this.Ping, this.document.getElementById('log'))); </script> </body> 


Living example: azproduction.ru/lpip (Please do not check for a long time)
Source: narod.ru/disk/6061703001/Ping.rar.html

Criticism, suggestions are welcome. I would be glad if someone writes a version of the ping server under nginx.

Source: https://habr.com/ru/post/114198/


All Articles