5

I have a simple chat app using socket io. Very similar to the socket io chat official demo. Socket io goes to ping timeout on all mobile browsers whenever the user minimizes the page or opens another app or page gets out of foucs.

In my web app whenever, the user clicks on file input upload from their chrome/firefox/safari browser on mobile, it opens up the phone dialog box to select the file from galley to select and upload. During this time (my chat page gets out of focus due to visibility), when user is searching for a file, and after exact 30 seconds, socket io client issues ping timeout, and the person chat is disconnected.

In my application level, i am doing a reconnect if socket io goes to ping timeout for whatever reason, but that doesn't fix my problem, because if the user clicks on 1 file after 31 seconds, then that image file is not sent in chat, because socket io was timed out during that time, and it takes few seconds to reconnect, when the user's focus is back on the browser tab. So the file upload action is lost, because file is uploaded but socket io connection was on ping timeout. so when it reconnects, the selected file upload is never sent to the other connected user.

I tried detecting whenever user's page focus is out, using this code,

document.addEventListener("visibilitychange", onchange);

from Is there a way to detect if a browser window is not currently active?

and then once page is not in focus, i used setinterval to emit my custom heartbeat event to my server code. (just to keep socket io from talking to server to keep connection alive).. This works fine for desktop browsers, But on all mobile browsers, whenever user's page is not in focus, i.e if a user minimizes the browser, opens a new app, or goto select file upload screen, socket io times out after 30 seconds or sometimes even faster then this.

My client js code:

var beating;
//function to detect if user gone out of page

//startSimulation and pauseSimulation defined elsewhere
function handleVisibilityChange() {
  //socket = this.socket;
  if (document.hidden) {
    // console.log();
    $('#logwrapper').prepend(" üser is outside \n ", socket.id);

    //emit on server after every 10 seconds
    beating = setInterval(function () {
      $('#logwrapper').prepend(" inside interval emitting");

      socket.emit('heartbeat', { data: "a" });
    }, 15000)

  } else {
    //console.log("üser is in");
    $('#logwrapper').prepend(" üser is in \n ", socket.id);
    console.log("çlearing inteval beating from visiblity");
    clearInterval(beating);
  }
}

document.addEventListener("visibilitychange", handleVisibilityChange, false);

And my server.js code


  //socket events for heartbeat 
    socket.on("heartbeat", function (data) {
      console.log("socket is outside ", data, socket.id);
    });
    //socket events for heartbeat , just log it so that my client remains active and keep communicating with server, to avoid socket io timeout

I also tried adjusting my socket io server & client timeout configs, but that is not making any difference, after user minimise the browser page on mobile, chorme, firefox & safari, socket io client emits ping timeout after 30 seconds or so,

How can i increase this timeout, so socket io connection remains active (don't ping timeout) for longer time (as much as i want)?

Here are my server.js configs for socket io:


var io = require("socket.io")(
  231, {
    transports: ["websocket", "polling"]
  },
  server, {
    path: '/socket.io',
    serveClient: true,
    // below are engine.IO options
    pingInterval: 25000, //was 10k, how many ms before sending a new ping packet, => how often to send a ping
    pingTimeout: 30000, //should be above 30k to fix disconnection issue how many ms without a pong packet to consider the connection closed
    upgradeTimeout: 20000, // default value is 10000ms, try changing it to 20k or more
    agent: false,
    cookie: false,
    rejectUnauthorized: false,
    reconnectionDelay: 1000,
    reconnectionDelayMax: 5000,
    maxHttpBufferSize: 100000000 //100 mb
  }
);

and my socket io client configs:


 this.socket = io.connect('https://appurl.com', {
      reconnection: true,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 5000,
      reconnectionAttempts: Infinity,
      //our site options
      transports: ["polling", "websocket"],
      secure: true,
      rejectUnauthorized: false,
      forceNew: false,
      timeout: 60000
    });

Basically on all mobile browsers, as long as page gets out of focus, my JS code stops executing, and whenever the page focused again, JS exection resumes, looks like its not a problem with setinterval or settimeout. How can i keep executing my client JS code to keep socket io from doing ping timeout?

I also tried a similar logic using settimeout, https://codepen.io/Suhoij/pen/jAxtn

this code works in codepen on all mobile browsers, even if i minimise this page on my mobile and come back again, the timer is still executing, but the same code when i run on my app, it doesn't work in my code. Why? Is socket io pausing my all client.js code when page is not in focus? OR is my logic not correct? I also tried emitting an event from my server.js after every 2 second to all clients, but the emits stop when page is not in foucs on mobile :/

Relevant socket io issues: https://github.com/socketio/socket.io/issues/2769

Faizan
  • 1,847
  • 8
  • 40
  • 63
  • Mobile browsers have gotten very aggressive about attempting to save battery power when a anything goes out of the foreground. Otherwise, you get a dozen apps all using a bit of battery and suddenly battery life is half what it could be. Regular apps (not browser pages) have the option of using the native platform push support which likely has fewer limitations. – jfriend00 Nov 02 '19 at 00:40
  • @jfriend00 so your suggestion to me is to go and make native apps for iOS and Android to avoid this issue? so there is no way to force this behaviour to keep socket io active even when page goes in background? Perhaps something in socket io configs can help me to increase the duration of this timeout to 1 min instead of 30 seconds? – Faizan Nov 02 '19 at 00:51
  • I don't know the best way to solve this problem in a browser page. I just know that mobile browsers have cracked down on anything that uses battery in background pages. socket.io in a browser is just Javascript that lives on top of the native webSocket and Ajax support so it doesn't have any capabilities to stay alive beyond what regular browser programming has. It can't break any of the rules that mobile browsers enforce. If you want to do this in a web page, I'd suggest you find some other web app that does something like this and look at the web page code to see how they do it. – jfriend00 Nov 02 '19 at 00:57
  • @Faizan I'm having the same problem! did you solve this? if yes then please give me some hints – basilisk Feb 04 '21 at 10:45
  • @basilisk if you check my answer it could help – willJk Mar 16 '22 at 20:47

1 Answers1

0

For what I had to do on socket.io 4.4.1

I used the manager events to handle disconnections on mobile https://socket.io/docs/v4/client-api/#event-error

    import io from "socket.io-client";
    socket = io(socketUrl, {
        reconnectionAttempts,
        reconnectionDelay,
    });

    //for the actual websocket having an error attempt to reconnect
    socket.io.on("error", (error) => {
        // ...
        console.log('socket io error on ' + error);
        socket = io(socketUrl, {
            reconnectionAttempts,
            reconnectionDelay,
        });
    });

    let socketArray = ['reconnect', 'reconnect_attempt', 'reconnect_error', 'reconnect_failed'];

    for(let i = 0, len = socketArray.length; i < len; i++) {
        socket.io.on(socketArray[i], (attempt) => {
            // ...
            console.log(socketArray[i] + ' socket io on ' + attempt);
        });
    }

This makes it if the websocket manager has an error it will reconnect. I had this happen when I was in the browser on the socket fine, shut off my iphone, went back to the browser where it reconnected using socket.on('connection' fine and then when I turned off my phone screen again and then went back in I would get this error WebSocket connection to 'wss://mysocketserver.com' failed: The operation couldn’t be completed. (kNWErrorDomainPOSIX error 53 - Software caused connection abort) by adding the socket.io.on("error" and having it reconnect it would then properly reestablish every thing

willJk
  • 111
  • 2
  • 4
  • 12