Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

socket.io stop re-emitting event after x seconds/first failed attempt to get a response

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 ?.

like image 803
Rainer Plumer Avatar asked Aug 21 '15 02:08

Rainer Plumer


1 Answers

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).

like image 64
jfriend00 Avatar answered Sep 23 '22 00:09

jfriend00