Starting Out With Comet (Orbited) Part 3 – The Client

Comet implementations require both server and client side components.  In Part 2 of this series we installed and configured the server side component and then used the example STOMP Test client to test it out and get a feel for what was going on.

In this part of the series we’ll be covering the client side to make our custom interface to the comet server.  To demonstrate we’ll go through the steps in creating an app I call EZChat which is basically a bare bones comet chat client.  The interface will allow you to choose a name and submit messages that will be broadcast to everyone viewing the page in real time.

For it to work you will have to have the configuration of Part 2 setup and your Orbited server running.  For reference I learned most of the client side code in this example by playing with the source of the example STOMP Test client. I recommend taking a look at that source if you need more advanced options or another example.

Includes

At the top of the <head> you first need to include the libraries for the Orbited (/static/Orbited.js) and STOMP (/static/protocols/stomp/stomp.js) client-side implementations.


<script>document.domain=document.domain</script>
 <script src="http://localhost:9000/static/Orbited.js"></script>
 <script>
 Orbited.settings.port = 9000;
 TCPSocket = Orbited.TCPSocket;
 </script>
 <script src="http://localhost:9000/static/protocols/stomp/stomp.js"></script>

 <script src="http://www.json.org/json2.js"></script>

In between the scripts we’ve set up a TCPScocket.  It must be don between the two script includes because the STOMP library needs the socket setup for its execution.  We’ve also specified the Orbited port which is necessary especially if you change the port on which the orbited and stomp javascript files are hosted on (they can both be hosted on port 80 along with your other scripts).

Lastly, we’ve also included a popular JSON library.  Typically I use jquery-json but I’ve kept this tutorial free of javascript frameworks so as not to add unneeded complexity.  If you’d like you can of course switch to whatever JSON library you’re used to; just replace the JSON.stringify and JSON.parse functions with your equivalents.

STOMP Setup

Because we’re dealing with the setup we’ll skip to the bottom of the page and add the following script just before the end tag of the body (</body>).


<script type="text/javascript">
(function() { // set up stomp client.

stomp = new STOMPClient();

 stomp.onconnectedframe = function() {  // Run on initial connection to STOMP (comet) server
 stomp.ready = true;
 // subscribe to channel CHANNEL = "/ezchat/"
 var CHANNEL = '/ezchat/'
 stomp.subscribe(CHANNEL);
 };

 stomp.onmessageframe = function(frame) {  // Executed when a messge is received
 my_receive( JSON.parse(frame.body) );
 };

 // Everything is setup. Start the connection!
stomp.connect(document.domain, 61613); //, 'guest', 'guest');
})();
</script>

My apologies as always for the crappiness of the wordpress syntax parser.   Lets walk through what’s happening.

At the top, we initialize a new STOMPClient object.  The StompClient has the following hooks you can override to trigger your own events.

onopen – Called when the Transport is opened
onclose – Called when the Transport has closed
onerror – Called when the Stomp Client has errored
onerrorframe – Called when there is an error in the message received
onconnectedframe – Called when a the client is fully set up for sending/receiving
onmessageframe – Called when a message is received

The STOMP object also has these functions for connecting and resetting the connections.

reset – Resets the STOMP connection
connect – Connects to the STOMP server
send – Sends the object in the first argument to the channel specified by the second argument

In our simple example only the onconnectedframe and onmessage frames need to be overwritten.

The onconnectedframe function is called when the STOMP server has been connected to and everything is setup for sending and receiving messages.  Inside this function we simply need to subscribe/listen to the CHANNEL we’ve setup for our chat.  For the example I’ve chosen the channel ‘/ezchat/’.  Once subscribed our STOMP client will receive any messages sent to that channel in real time.  You can subscribe to multiple channels if you’d like, and you can make clients with different channels if you’d like to have different chat rooms.  But for this example we’ll just stick with the hard coded ‘/ezchat/’ channel.

The onmesageframe function is called when a message has been received.  It is passed a frame object with the following structure

frame

  • body: “{“name”:”Dave”,”message”:”awesome this is working”}”
  • headers: Object
    • content-encoding: “utf-8”
    • content-length: “51”
    • content-type: “text/plain”
    • destination: “/ezchat/”
    • message-id: “/ezchat/_3”
  • type: “MESSAGE”

where the body holds the information that has been sent.  The STOMP server and client add extra “type” and “headers” objects to communicate between each other.  The extra information can be very useful for more complicated applications but for our simple example we’re only interested in the frame “body”.

So you see that the onmessageframe is simply parsing the json object in frame.body of every received message and passing it to my_receive, a function we will soon create.

The Content

For EZChat we need a form where users can specify a name and type messages to send.  We also need an area to put the messages.  Plop this HTML in at the top of the <body> to handle all of that.

<h2>EZChat - Example Comet Client!</h2>
Everyone viewing this page will see the messsages you submit instantly.
<form id="message_form" action="#">
 Name:
 <input type="text" name="chat_name" id="chat_name"></input>
 Message:
 <textarea name="message" id="message" rows="4" cols="40"></textarea>
 <input type="submit" name="Send" onclick="return my_send(); return false"></input>
</form>
<div id="messages"><!--- All received messages will get placed here ---></div>

There are a few things to notice here.  First, the important elements in the form have ids ‘chat_name’ and ‘message’ and the area that will be containing all the received messages is called ‘messages’.  The names don’t matter except that we’ll use them in the functions we create later.

Second, the onclick event of the submit button is overridden with instead calling the my_send function.  We’ll make this function in the next step.

The Functions

Lastly we need to make the custom my_send and my_receive functions that get called to send and receive messages.  Insert these functions into the head after the includes.

my_send

The my_send function will get the values from the ‘chat_name’ and ‘message’ form elements, combine them in an object, convert the object to json, and then sends it to the ‘/ezchat/’ channel.  The sending is handled using the stomp.send command which takes as input the object to send and second, the channel to send it to.

var CHANNEL = '/ezchat/';
function my_send() { 

// Get the values to send from the form
 var name = document.getElementById('chat_name').value;
 var message = document.getElementById('message').value;

 var msg = {'name': name, 'message': message};

 var json_msg = JSON.stringify(msg);
 stomp.send(json_msg, CHANNEL)
 return false;
 }

my_receive

As discussed earlier the my_receive function gets the JSON parsed version of whatever was sent in ‘frame.body’. In the case of our app its always an object of the format

msg = {‘name’: <some name>, ‘message’: <some message>}

The my_receive function simply takes this object and converts it into a prettier HTML format and appends it to the top of the message list we created in the HTML.

function my_receive( msg ) {
 console.log('received message', msg);
 // append the <msg> to the top of the list of messages.
 var messages_el = document.getElementById('messages');
 var new_message = "
<div><strong>" + msg['name'] + ":</strong> " + msg['message'] + "</div>
";
 messages_el.innerHTML = new_message + messages_el.innerHTML;
 }

That’s it for the code.  Scroll to the bottom of the page for the full version of the source.

Results

Ensure that your Orbited server is running as described in Part 2 of the series.  Then load up the page we’ve made in two or more separate windows.  Choose a different name for each window and start sending each other messages.  You’ll notice that both windows will receive the submitted messages almost instantaneously!  The beauty of Comet!

ezchatIf you open your STOMP Test Client and subscribe to the ‘/ezchat/’ channel you’ll see a more raw input on what’s actually being received by the STOMP clients as you chat.

ezchatstompYou can see how the STOMP Test client is incredibly handy for debugging.

That’s it for this part of the Tutorial!  There are still parts to come including writing a data handler on the server side and hopefully a much requested post on Django integration.

Full Source

For your convenience here’s the full index.html file for this example.


 <html>

<head>
 <script>document.domain=document.domain</script>
 <script src="http://localhost:9000/static/Orbited.js"></script>
 <script>
 Orbited.settings.port = 9000;
 TCPSocket = Orbited.TCPSocket;
 </script>
 <script src="http://localhost:9000/static/protocols/stomp/stomp.js"></script>

 <script src="http://www.json.org/json2.js"></script>

 <script type="text/javascript">
 // These are our custom functions for sending and receiving STOMP messages.
 // They will be sent in the format msg = {'name': somename, 'message': somemessage}

 var CHANNEL = '/ezchat/';

 function my_receive( msg ) {
 console.log('received message', msg);
 // append the <msg> to the top of the list of messages.
 var messages_el = document.getElementById('messages');
 var new_message = "
<div><strong>" + msg['name'] + ":</strong> " + msg['message'] + "</div>
";
 messages_el.innerHTML = new_message + messages_el.innerHTML;
 }
 function my_send() {
 // Get the values to send from the form      

 var name = document.getElementById('chat_name').value;
 var message = document.getElementById('message').value;

 var msg = {'name': name, 'message': message};
 console.log(msg);

 var json_msg = JSON.stringify(msg);
 console.log(json_msg);
 stomp.send(json_msg, CHANNEL)
 return false;
 }       
 </script>

</head>

<body>
<h2>EZChat - Example Comet Client!</h2>
<div>Everyone viewing this page will see the messsages you submit instantly.</div>
<form id="message_form" action="#">
 Name:
 <input type="text" name="chat_name" id="chat_name"></input>
 Message:
 <textarea name="message" id="message" rows="4" cols="40"></textarea>
 <input type="submit" name="Send" onclick="return my_send(); return false"></input>
</form>
<div id="messages"><!--- All received messages will get placed here ---></div>
<script type="text/javascript">
(function() { // set up stomp client.
 stomp = new STOMPClient();
 stomp.onconnectedframe = function() {  // Run on initial connection to STOMP (comet) server
 stomp.ready = true;
 // subscribe to channel CHANNEL = "/ezchat/"
 stomp.subscribe(CHANNEL);          
 };

 stomp.onmessageframe = function(frame) {  // Executed when a messge is received
 console.log('frame is', frame);
 my_receive( JSON.parse(frame.body) );
 };

 // Everything is setup. Start the connection!
 stomp.connect(document.domain, 61613); //, 'guest', 'guest');
})();
</script>

</body>

</html>

As usual, if there are corrections or questions please be sure to leave them in the comments. And remember to subscribe to catch the rest of the series.

Advertisements
18 comments
  1. @johnny. Nope I haven’t tried it as it uses rails. Would be great if someone made a version for Django 🙂

  2. kevin said:

    hei dave
    thanks for the tutorial

    does this html source work in ie7 and ie8? were you able to test that?
    thanks!

    • @kevin, I didn’t test on IE but it “should” work there as well. My components are pretty simple, and the Orbited code is tested to work on all browsers.

  3. Johnny said:

    Dave,

    Great posts! One thing I had trouble with was that I had a JavaScript error. I know about zero js so it was a bit of a pain to figure out. Turns out that the line new_message was never being assigned due to an error. To fix this I just made the 3 lines below into one line.

    24. var new_message = ”
    25. ” + msg[‘name’]…etc
    26. “;

    Was this intentional to break the lines up for some browser effect? I was using firefox 3.5.8 and an old chrome version.

    Also, I could not for the life of me figure out where the default static directory is since you don’t state where I put this html file we created. I ended up defining

    [static]
    example=example.html

    in the config file. Anyways…thanks so much for the well written post.

    One thing about IE6. I was never able to get IE to work even though it worked fine on chrome and firefox. I tried decreasing any IE security/script setting and restarted IE, but gave up since I really don’t use IE. That’s too bad since many companies still only support IE.

    Johnny

  4. In the case of developing a one to one chat application ( 2 users only ) , is it performant two users subscribe to a one channel? . What would be recommended in that case ?. I need make a web chat applications like facebook.

  5. haridas said:

    Hi Dave,

    I’m a also new to this technology and your blog helped me to work with the stomp test module come along with orbited and also setup your ezchat with some tweaks.Feeling good now and got a base on the orbited.Now want to play with my project…:).

    Have a nice day.
    Haridas.

  6. Milad said:

    Hi Dave,
    I am new to cometd and stomp. my server uses cometd and I need to develop an app for iphone which get feeds from cometd server using stomp , can you help me in this problem , I dont know where to start …

  7. Phuongnt said:

    I have a question:
    when I run stomp test in address: http://localhost:9000/static/demos/stomp/
    It works well.
    When I run the html file directly from harddisk or move the test folder to another web server, it cannot work.
    For example: http://localhost:80/stomp/ (Apache server, I’ve already changed the path of js file in index.html)
    How can I configure Orbited to serve Site from another webserver, Any one can help me? Hope to see answers soon : )

  8. czytelnik said:

    Hi, is orbited dead or still developed? Site is down, only Orbited2 is online, but has no documentation :/
    Is there any replacement for it?
    I need it with Python/Django

    • I’m not sure about the online status. It’s certainly not as popular these days. Much of the focus in the python community has gone toward the excellent Tornado framework/server. I highly recommend checking that out. It’s much easier to get working with Django than Orbited.

      • Felix said:

        Hi Dave,

        My understanding of Tornado is that it is a totally different python frame work just like django. I have looked around for about one week but still didn’t figure out how to make Django work with Tornado. Is it something like making django hosted on Tornado or it’s more like the relation between django and orbited – running these two on two servers, let django take care of the logic and let both the webpage and the server send messages to orbited for message passing? Why tornado+django is much easier than orbited+django? Thank you!

        Felix

  9. Felix said:

    Hi Dave,

    Thanks for your tutorial! This helped me learn about how to use Orbited. However, when I tried to use this along with my site built with django, it doesn’t work. The problem is that “stomp.connect(document.domain, 61613); ” doesn’t seem to connect to the stomp server at all (when running the stomp demo, when connect a webpage to the orbited server, there will be a line “ACCESS connection closed from 127.0.0.1:59311 to localhost:61613” printed in the terminal, but for my site nothing outputed). I have used firebug to dig into the code and find out the code stops running after line 866 of Orbited.js which is “session.open(sessionUrl.render());”. I digged further into .open method for both running stomp demo and my site and didn’t really see differences on the stack.

    My guess of the reason is that the stomp demo has the static pages hosted on localhost:9000 which is the same server as the stomp service while my django site is hosted on localhost:8000 and on a different server. But the part confuses me is that when I follow the js code running, I didn’t see any part that can tell whether the page is hosted on orbited server or django server. Have you ever met this situation before?

    • Felix said:

      I just tried moving the html file generated by django to orbited’s static folder, every thing works just fine. So I guess my problem is similar to Phuongnt’s posted on 11/26/11.

    • Hi Felix,

      It’s been so long since I used orbited that I can’t recall.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s