Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rate-limiting the data sent down WebSockets

We're sending a lot of data down a websocket (from a Node.js app to the web browser).

The data is binary data in the form of blobs.

Occasionally, the end-user is on a poor connection - and in this case, we'd like to 'skip' messages (leave them out) and make sure we don't cram down more data than the user can receive.

On the server side, we have tried:

function sendBlob(blob, socket) {
  console.log('socket.bufferedAmount: ' + socket.bufferedAmount); // Always zero

  if (socket.bufferedAmount > 0) {
    return; // Never called
  }

  socket.send(blob);
}

Unfortunately bufferedAmount always returns zero.

Is this the right way to see how much data is being queued but not sent/received in websockets, or is there a better way to achieve this?

(Have also tried logging socket.bufferedAmount on the client-side, but it also always returns zero).

like image 570
Chris Nolet Avatar asked Dec 08 '22 12:12

Chris Nolet


1 Answers

The socket.bufferedAmount property that exists on clients (as well as the ws module for Node) is the amount of bytes that it itself has buffered, not the remote. That means socket.bufferedAmount on the server means how many bytes that are waiting to be sent to the client, and for the client it is how many bytes are waiting to be sent to the server.

The reason you aren't getting any change in the property is that your network is probably indeed sufficient to deliver the data. If you actually want to see a difference in socket.bufferedAmount, then try throttling your browser network access. This can be done with browser extensions or tools like NetLimiter.

If you want to throttle connections by skipping messages, you can think about creating some type of heartbeat system between the client and server. There are many ways you could do this, such as applying this function:

setInterval(function() {
  if (socket.bufferedAmount == 0) {
    socket.send('heartbeat');
  }
}, 1000);

And then detecting missed heartbeats by counting the time interval. This is rather inefficient, but there's also other ways to do this such as responding to sent data from the server (although take into consideration that if you want to send a heartbeat when receiving data, the heartbeat itself might get throttled or other side effects).

An alternative solution would also be available if you were willing to switch to Socket.IO. It has a feature that allows you to send volatile messages, which are messages that are dropped if the client is busy or is not able to accept messages for any reason.

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  var timer = setInterval(function () {
    socket.volatile.emit('data', 'payload');
  }, 100);

  socket.on('disconnect', function () {
    clearInterval(timer);
  });
});

Do note that Socket.IO will be heavier on your application, and doesn't use the native websocket protocol. It will utilize websockets when it is an option, but it is one of many transports. Socket.IO is built on Engine.IO, which uses a fork of the module you're currently using.

like image 122
hexacyanide Avatar answered Dec 11 '22 09:12

hexacyanide