A JSON event-based convention for WebSockets
HTML5 WebSockets are cool. Given a compliant server - and browser -, all you need to do is instantiate your socket object and start listening to server-pushed data.
1 var socket = new WebSocket('ws://socket.server.com'); 2 3 socket.onmessage = function(evt){ 4 alert("I got data: " + evt.data) 5 }
1 socket.send( "Thank you, Mr. server!" );
1 // Incoming messages 2 socket.onmessage = function(evt){ 3 // We use jQuery because life's too short for raw DOM scripting 4 $('#messages').prepend( "<li>" + evt.data + "</li>" ); 5 } 6 7 // Send message to chat server 8 $('form').submit(function(){ 9 socket.send( $(this).find('input.message').val() ); 10 return false; 11 });
SEND destination:/some/channel hello everybody ^@
It's got a command name, a destination header (a channel, chat room, topic, etc) and a body (the "^@" at the end means "end of the message"). It's up to client implementations to parse this by splitting each part and do something with it.
See where I'm going with this? You could implement a STOMP Javascript client and make it talk to a STOMP server over WebSockets (here's how).
But who would want to do such a thing? Parsing sloppy text strings to make out what the server wants from you? No fun.
We might as well use JSON.
JSON is Javascript, Javascript is JSON. Decent browsers even come with built-in JSON parsers! Lets have our imaginary chat server send us JSON strings like this one:
1 [ 2 "user_connected", 3 { 4 "name" : "Ismael Celis", 5 "message" : "Yo", 6 "twitter" : "ismasan" 7 } 8 ]
This is a made up message format, but it's useful to our purposes: it's a JSON array where the first element is an event name "user_connected" and the second element is the event "data", in this case a JSON object with some properties. In our particular application some of these properties might be mandatory (such as name and message), whereas others can be optional ([event_name, data_object] general message format. I'll show you why.
Standard, mini-protocol, call it what you like. The fact is that this tiny convention for server-browser communication brings about all sorts of awesomeness to our little Javascript application. We can now wrap message-parsing in a neat, Javascript-y object with it's own event semantics and everything. This is how it looks:
1 var server = new ServerEventsDispatcher(); 2 3 server.bind('user_connected', function(user){ 4 $('#messages').prepend( "<li class='connected'>" + user.name + " has connected!</li>" ); 5 }) 6 7 server.bind('user_message', function(data){ 8 $('#messages').prepend( "<li>" + data.name + " says: " + data.message + "</li>" ); 9 })
- WTF just happened?!
You courteously inquire.
See that ServerEventsDispatcher thing? I just made it up. Go on, take a look. It's only 30 lines.
Quite trivial, isn't it? It wraps the native WebSocket object you've come to know and love during the last 10 minutes. It then implements it's own event-binding and triggering mechanism and pipes said events from and to the server as JSON-encoded strings in the format we just defined.
In fact, you can even hook in multiple handlers to the same event, already an improvement over WebSocket semantics!
1 server.bind('user_connected', function(user){ 2 $('#messages').prepend( "<li class='connected'>" + user.name + " has connected!</li>" ); 3 }) 4 5 var user_count = 0; 6 7 server.bind('user_connected', function(user){ 8 user_count++; 9 $('#user_count').html( user_count + " users currently connected"); 10 })
You can use the same mechanism to add handlers to the native WebSocket events open, message and close.
1 server.bind('close', function(){ 2 $('#status').prepend( "Connection closed by server" ); 3 })
... And you can send messages ("events", in our idiom) back to the server and to all connected users. The underlying method is called "send" in the WebSocket API, but I've chosen to call it "trigger" so we stay close to the idea of events. I like that because Javascript in the browser is all about events ("click", "mouseover", "submit") and I really like the idea of treating the server just like you would any other DOM element.
1 // Send message to chat server ... By "triggering" the user_message event. 2 $('form').submit(function(){ 3 4 server.trigger( 5 'user_message', 6 { 7 name: 'Ismael Celis', 8 message: $(this).find('input.message').val() 9 } 10 ); 11 12 return false; 13 });
This is just how jQuery events API works, by the way, and this little pattern fits quite well with the bigger picture of Javascript applications.
It's not limited to boring chat applications, either. The following example illustrates sending mouse coordinates for, say, moving your character around in the next generation of Call of Duty, Javascript Edition:
1 // Broadcast your mouse movements to other players 2 3 $('body').mousemove(function(evt){ 4 server.trigger('player_move', { 5 player_name: 'Ismael', 6 x: evt.clientX, 7 y: evt.clientY 8 }); 9 }); 10 11 // Listen to other players moves and update their characters on screen. 12 13 server.bind('player_move', function( move ){ 14 $('#player_' + move.player_name).css( {left: move.x, top: move.y} ); 15 });
The one thing to take from all this? WebSockets are important. You can add them easily to your web applications and build your own, domain-specific protocols on top of them. AND IT'S ALL JAVASCRIPT.
I hope you are excited by now.
UPDATE: I've uploaded slides from a presentation I did some days ago around this subject. You can see them here.