Socket.IO WebSocket Frame Optimisation

Socket.IO is not the most efficient way of serving up WebSockets. There are lightweight options out there such as the ws library that offer a simple WebSocket abstraction without the additional overhead required to handle things like fallback transport layers (such as polling) and other chat-room concepts like rooms and namespaces. But if you already have an application using Socket.io (like me) – then this could be useful. Note: binary WebSockets are out of the scope of this post. This would be a more efficient way of creating a custom communication protocol for the browser. This post is purely a summary of what I’ve found when trying to condense typical Socket.IO events as much as possible.

Remember, to force WebSockets you must specify this as the only transport layer in the client config, as so:

io({
  transports: ['websocket']
});

A typical WebSocket frame sent by socket.io for an event looks like this:

42/mynamespace["event","value"] (31 bytes)

The first two bytes form part of engine.io’s and socket.io’s framing protocol, so they’re here to stay. More info on this here. 

Don’t use namespaces unless necessary: if you inspect a WebSocket frame sent on a connection using a custom namespace, the entire name of it is sent along with each event. So if you absolutely have to use one, then my advice is to keep it short.

42["event","value"] (19 bytes)

Keep event names short: come up with a simple communication protocol that maps integer event names to something human readable upon delivery. I even got away with using an integer as an event name and it worked.

42[0,"value"] (13 bytes)

Use integers for commands instead of strings: if you are sending a simple event that can be interpreted on either side by an integer, then this is the biggest optimisation. This will add as little as one byte (depends on the number) where as sending a string would be however many bytes the string is, plus the double-quotes surrounding it.

42[0,1] (7 bytes)

Sending larger payloads > ~80 bytes

Consider emitting binary events (supported by Socket.IO) if your frame is at least over ~50 bytes (depending on your event name length). Based on my research it is not worth it for anything less as Socket.IO sends a preceding instruction frame of this size before the binary frame is sent.

In this example we saved 24 out of initial 31 bytes. It’s a saving worth making especially when low latency is key (always) and streaming a high volume of events to connected users.

Also less bandwidth = less money.

Leave a comment

Your email address will not be published. Required fields are marked *