Movatterモバイル変換


[0]ホーム

URL:


Skip to main content
RabbitMQ is developed byVMware Tanzu. Get 24/7 enterprise support backed by the core engineering team.

How to compose apps using WebSockets

· 4 min read
Marek Majkowski

Or: How to properly do multiplexing on WebSockets or on SockJS

As you may know, WebSockets are a cool new HTML5 technology whichallows you to asynchronously send and receive messages. Ourcompatibility layer -SockJS - emulates it andwill work even on old browsers or behind proxies.WebSockets conceptually are very simple. The API is basically:connect, send and receive. But what if your web-app has many modulesand every one wants to be able to send and receive data?

In theory you could open multiple WebSocket connections, one for everymodule. Although suboptimal (due to the need to handle multiple TCP/IPconnections), this approach will work for native WebSockets. But,unfortunately it won't for SockJS due to a technical limitation ofHTTP: for some fallbacks transports it is not possible to open morethan one connection at a time to a single server.This problem is real and worth solving. Let me rephrase it:

Assuming you can have only a single connection to a given host, and multiple modules wanting to send and receive data, what do you do?

You needmultiplexing:combining data from multiple sources into a single connection. Thenext question is what API do you use; how do you expose multiplexingin the code?

The Socket.io way

Socket.io has an API that attempts to solve this problem, it callsthis 'namespaces'. Here's some example client (browser) code:

var chat= io.connect('http://localhost/chat');
chat.on('connect',function(){
chat.emit('hi!');
});

var news= io.connect('http://localhost/news');
news.on('news',function(){
news.emit('woot');
});

I think this API is quite confusing - under the hood Socket.io isopening only a single connection, but reading the code gives us adifferent story.

The SockJS way

As opposed to Socket.io, SockJS doesn't have any magical API. It lookslike a WebSocket object, it behaves like one. Nothing surprising.

So how to solve the multiplexing problem?

It's usually a good idea to avoid inventing new APIs if possible, byusing already established ones. Why not present each multiplexedchannel as a WebSocket object?

What I'm suggesting is quite simple - you take a real SockJS (orWebSocket) connection, wrap it in a multiplexing layer, and extractany number of fake WebSocket objects out of it. They will bemultiplexed internally, but from a module point of view - it will becompletely transparent. The module speaks to a WebSocket object as far asit is concerned.

That's it. It's a bit like a magician's hat. You put one WebSocketconnection in, you can take any number of fake WebSocket connectionsout.

This approach is better than what Socket.io proposes - you can createcode that just relies on a native WebSocket API. Later on, when theneed arises, you can just pass a fake WebSocket object instead of realone. In other words: it composes. Problem solved.

Implementation

If previously in the browser you were using a single SockJSconnection, like this:

var sockjs=newSockJS('/echo');

You can modify the client code to:

var real_sockjs=newSockJS('/echo');

var multiplexer=newWebSocketMultiplex(real_sockjs);
var fake_sockjs_1= multiplexer.channel('ann');
var fake_sockjs_2= multiplexer.channel('bob');

At this point 'fake' objects will behave identically to a normalSockJS object. You can expect to hear 'open', 'message' and 'close'events.(The underlying code isabout 60 lines of javascript)Similarly the server side - it normally uses the usual "net.Server" and"Stream" node APIs:

var service= sockjs.createServer();

After a change:

var real_service= sockjs.createServer();

var multiplexer=newmultiplex_server.MultiplexServer(real_service);
var fake_service_1= multiplexer.registerChannel('ann');
var fake_service_2= multiplexer.registerChannel('bob');

Again 'fake' objects will do the usual thing, they will emit a'connected' event when a user subscribed to this particular channelarrives.(The underlying multiplexer codeis not very complex either)

If you want to see the multiplexer code in action:

Final thoughts

It's worth emphasising that this approach really does compose. Any modulecan take a fake WebSocket object and repeat the trick to get moresecond-layer fake WebSockets objects.Instead of inventing new API's, just create code that relies on aWebSocket instance passed to the constructor. That'll all you reallyneed to create composable code using WebSockets!


[8]ページ先頭

©2009-2025 Movatter.jp