Moving Forward

Homepage of Andrew Robinson

Effectively using Socket.io

without comments

Socket.io is one of the new technologies on the block when it comes to interactive realtime client-server messaging. It’s a library, that true to its name, tries to embody the traditional properties of sockets. It handles a lot of the low-level details required to establish fast bidirectional communication between a client and a server, and makes implicit guarantees of reliable, sequential transmission in the bold statement found on their home page of ’100% care-free realtime’.

Like most of these new-fangled web technologies the homepage is intentionally sparse, with a trendy ‘Fork me on GitHub’ link in the top corner, a few code snippets showing how amazing it is, and a three question FAQ section. This is the brave new world of technologies. They move so fast there’s little time for the developer to spend properly documenting. The trend nowadays is to throw the entire source up on GitHub, along with a few examples, and toss it to the masses.

Unfortunately, this gives the developer little guidance on its proper usage or implementation. The main page gives a deceptively simple implementation, but to really effectively use this technology, you need to do a little bit more work.

Do Not Trust Socket.io

On foreign policy Ronald Reagan had a very concise policy, summed up by his infamous quote, “Trust, but verify.” The same principle applies to socket.io. You must not, at any point in time, assume that socket.io will provide a reliable communication tunnel between your client and server.

This seems a little odd, after all, some of their extremely sparse documentation mentions how socket.io has great error handling facilities, and there’s even mechanisms in place to ensure reliable transport. If socket.io has these built-in already, then why bother to reimplement them?

Well, socket.io doesn’t know anything about your application. You are building something with very specific requirements. It might need to be super-responsible, where a second long delay (or longer) could make or break the experience, or it might need to be super-dependable, with a requirement that every single message needs to be passed without fault, but speed isn’t quite so important. Your application’s transport requirements are unique, and there’s just no way that what’s built into socket.io can fit your needs appropriately.

To properly fulfill the unique requirements of your application, you’ll find yourself implementing algorithms and procedures to ensure receipt of packets, and to ensure the connection channel stays open.

In addition to meeting the unique requirements of your application, another reason to implement your own verification of delivery is because no where is it guaranteed that socket.io is implemented correctly, or will handle all the errors a websocket could face. By handling things in the application level, you gain a level of safety. It’s very probable that socket.io might not catch an edge condition, or will silently fail for a long time before delivering an error, at which point you might not be sure what messages have been successfully delivered.

Why use it at all then?

If using socket.io requires so much work on the developer’s part, it might be tempting to ditch it all together, and use traditional websockets, or some other technology. I wouldn’t recommend this at all. The folks behind the project have done a very fantastic job supporting a large number of browsers and transports. You will not be able to, in a reasonable amount of time, be able to put together an implementation anywhere near as clean as these guys have. One area they have really nailed is the feature-detection and abstracting the interface above the specific transport, such that an app can use websockets, flash plugins, or long-polling pretty transparently depending on what the browser might support. It’s truly fantastic work. Additionally handling many clients is done pretty elegantly, as is the entire API.

Why do they bother in the first place?

So, with this argument, one must wonder why the guys behind socket.io bother to implement any sort of guarantees at all. If all of this should be done in the application level, why bother with transport level implementations? I think there’s a couple of reasons.

Rapid Prototyping

The most obvious is that by building these features into socket.io, it enables rapid development of prototype applications, and does a reasonably good job of delivering on the promises for a broad variety of use cases. For times when a quick prototype is needed, the developer isn’t often thinking about guaranteeing the delivery of message, they are usually more focused on actually making the prototype work at all. Having this guarantees built it makes their life a little easier at this stage.

Internal Book-keeping

Another very useful reason for features specifically like heartbeats is that it helps socket.io maintain internal state. Taking a look at the Node.js implementation you’ll realize that socket.io does actually have to maintain a few bytes of state for each client. Because it’s transport agnostic, and some of the transports have no concept of a session, while others don’t really detect sudden disconnects, a heartbeat is a great way to check if a session is still active, and if it’s not, clean up associated state. You can use the disconnect events in a similar fashion if you wish, but don’t rely on them being triggered.

The End-to-End Argument

Finally, I’ll finish off this post by mentioning that the point made here is a pretty direct application of the end-to-end argument, originally made by J. H. Saltzer in 1984 and one of the classic ideas in systems design. I’d recommend everyone take a read of this paper, the ideas presented will help guide you in implementing any system.

Written by Andrew Robinson

January 18th, 2012 at 7:38 am

Posted in Web

Leave a Reply