10

I noticed that whenever my server is offline, and i switch it back online, it receives a ton of socket events, that have been fired while server was down. ( events that are ... by now outdated ).

Is there a way to stop socket.io from re-emitting the events after they have not received a response for x seconds ?.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
Rainer Plumer
  • 3,693
  • 2
  • 24
  • 42
  • Did you switch your server back online after a few seconds and then received all the events that happened during the time your server was offline? i am curious as to how much time passed between your server restart. – Rahat Mahbub Aug 27 '15 at 08:56
  • It seems that it doesn't really matter how long the server is down. Ive waited around 10 minutes or so, and the socket events still come in when the server comes back online. ( this happens only if users browser/tab is opened ). The longer server is down, the more events it receives. – Rainer Plumer Aug 27 '15 at 17:16
  • That is unusual behavior. It should only retry for the first 20 seconds. Can you post some of your client side and server code – Rahat Mahbub Aug 27 '15 at 23:41
  • Is this something a socket.disconnect event could solve? – Tyler Davis Aug 28 '15 at 00:06

2 Answers2

30

When all else fails with open source libraries, you go study the code and see what you can figure out. After spending some time doing that with the socket.io source code...

The crux of the issue seems to be this code that is here in socket.emit():

  if (this.connected) {
    this.packet(packet);
  } else {
    this.sendBuffer.push(packet);
  }

If the socket is not connected, all data sent via .emit() is buffered in the sendBuffer. Then, when the socket connects again, we see this:

Socket.prototype.onconnect = function(){
  this.connected = true;
  this.disconnected = false;
  this.emit('connect');
  this.emitBuffered();
};

Socket.prototype.emitBuffered = function(){
  var i;
  for (i = 0; i < this.receiveBuffer.length; i++) {
    emit.apply(this, this.receiveBuffer[i]);
  }
  this.receiveBuffer = [];

  for (i = 0; i < this.sendBuffer.length; i++) {
    this.packet(this.sendBuffer[i]);
  }
  this.sendBuffer = [];
};

So, this fully explains why it buffers all data sent while the connection is down and then sends it all upon reconnect.

Now, as to how to prevent it from sending this buffered data, here's a theory that I will try to test later tonight when I have more time.

Two things look like they present an opportunity. The socket notifies of the connect event before it sends the buffered data and the sendBuffer is a public property of the socket. So, it looks like you can just do this in the client code (clear the buffer upon connect):

// clear previously buffered data when reconnecting
socket.on('connect', function() {
    socket.sendBuffer = [];
});

I just tested it, and it works just fine. I have a client socket that sends an increasing counter message to the server every second. I take the server down for 5 seconds, then when I bring the server back up before adding this code, all the queued up messages arrive on the server. No counts are missed.

When, I then add the three lines of code above, any messages sent while the server is down are not sent to the server (technically, they are cleared from the send buffer before being sent). It works.


FYI, another possibility would be to just not call .emit() when the socket is not connected. So, you could just create your own function or method that would only try to .emit() when the socket is actually connected, thus nothing would ever get into the sendBuffer.

Socket.prototype.emitWhenConnected = function(msg, data) {
    if (this.connected) {
        return this.emit(msg, data);
    } else {
        // do nothing?
        return this;
    }
}

Or, more dangerously, you could override .emit() to make it work this way (not my recommendation).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks to both of you for the question and answer. Saved much time investigating this ourselves. I can confirm success with the "on connect, clear sendBuffer" approach. In my case, I did "sio.on('connect', function(data) { sio.sendBuffer.length = 0; ... " and it worked wonderfully, with no apparent bad side-effects, using socketio 2.1.2. – Peter Hansen Mar 21 '18 at 19:48
  • @jfriend00 very nice answer but socket.sendBuffer seems to not work with npm socket.io v2.3.0. I have a route where I emit some code to the user, when something is wrong, user is redirected to start page, where he need to fill input field again (lets hope with proper data this time, so he wont be redirected again). My problem is that when he use the same route, all sockets are emitted TWICE - even with use of socket.sendBuffer = []; Any thoughts on this? Thanks! – b4rtekb Jun 29 '20 at 14:48
  • @b4rtekb - We'd have to see your actual code to know what to suggest. Please write your own question that includes your specific code. Usually double emits are because you accidentally have double event handlers somewhere. – jfriend00 Jun 29 '20 at 21:02
  • Great! Here is the question - https://stackoverflow.com/questions/62679013/socket-io-double-emits-when-executing-express-v4-route I'm always available at bartekb@gmail.com, thanks. – b4rtekb Jul 01 '20 at 14:24
  • @jfriend00 I have a similar question about re-emit, but it is for server side. Could you please take a look and maybe shed some light on it ? Thanks! https://stackoverflow.com/questions/62992888/how-do-i-let-socket-io-server-re-emit-to-particular-socket-when-that-socket-reco – Qiulang Jul 21 '20 at 11:02
0

Volatile events are events that will not be sent if the underlying connection is not ready (a bit like UDP, in terms of reliability).

https://socket.io/docs/v4/emitting-events/#volatile-events

socket.volatile.emit("hello", "might or might not be received");