Friday, June 8, 2012

Building HelloSun, Step 4: Real-Time Chat

HelloSun: A guide to creating a social networking empire, from scratch, for free, circa 2012.
⇐ Step 3 T.O.C. Step 5 ⇒

HelloSun is going to be a kind of chat app, so for the next step let's make a very very very basic real-time basic chat page. It seems that the first thing anyone does when doing a node server is a chat app, so there are a whole lot of examples to borrow from.

For the first chat page I’ll steal liberally from the example at https://github.com/jmg/node-simple-chat, making only minimal changes to make it work with Heroku and with the jade template language (for no other reason than I’m looking for excuses to learn more about Heroku and jade).

choosing socket.io for real-time(ish) push

A real-time chat app is one where as soon as somebody enters a message, everyone else on the chat sees it right away. In the traditional browser model, the browser client is not updated until the browser pulls data from the server (e.g. through a page refresh), but in this use it makes more sense to think of the browser pushing data to the client when it's available. Over the years many tricks have been developed to make it seem like the server can push data (many documented at Wikipedia Push Technology), and even more options are available as WebSockets become standardized.

The existing push technologies are a big bag of kludgy tricks that vary depending on the capabilities of the exact browser being used, and so I'm very glad that someone has bundled up a library of code to manage all of these differences once and for all, in a library called socket.io. The the socket.io javascript library provides a single simple interface to direct communication between the client and the server, hiding whatever different methods are used to pull off the trick. (Thank you very much, Guillermo Rauch!) Another nice feature of socket.io is that the API for the client and for the server are nearly identical (which is the "client" and which is the "server" doesn't matter any more).

socket.io has just two primary methods that we care the most about:
  • emit() sends a message to the other end of the connection
  • on() receives an event (usually a message from the other end of the connection)
Installing socket.io

socket.io is under rapid development, but I believe I've found one that works stably on Heroku. To use this version of socket.io add this dependency line to your "package.json" file:

  "socket.io": "0.9.6"

then run “npm install -d" to cause the latest version of socket.io to be installed.

chatserver.js - server-side chat code

The server side of this chat server is in the new file "chatserver.js" (see Getting the code below, or view chatserver.js). "chatserver.js" is almost directly taken from the "chat.js" file at the node-simple-chat project we're plagiarizing. The primary change is in these lines:

  io.configure(function () { io.set("transports", ["xhr-polling"]);
  io.set("polling duration", 10); });


which are taken from Heroku's advice at Using Socket.IO with Node.js on Heroku to prevent socket.io from trying tricks that Heroku doesn't support.

This "chatserver.js" code, after connecting with the client, simply receives JSON strings labelled as "msg" (we could have picked any label, so long as it matches label the client sends), and appends that to the list of messages, and broadcasts the message to all connected clients. Pretty straightforward.  (I'm not a fan of the "Array.prototype.inject" trick in this code, but plagiarizers don't have much right to complain.)

To load "chatserver.js", the file "app.js" (see Getting the code below, or view app.js) gets this line added at the end:

  var chatserver_io = require('./chatserver').start(app);

chatty.jade - client-side chat code

The client side of the chat server is in the new file "views/chatty.jade" (see Getting the code below, or view views/chatty.jade)  "chatty.jade" is almost directly taken from the "chat.html" file at the node-simple-chat project we're plagiarizing, only changed to support the jade templating library.

"chatty.jade" is straightforward. It collects input, sends it to the server (with socket.emit('msg',...)), and displays in the chat window any chat messages it received from the server (with socket.on('msg')).

Warning: including 'socket.io.js' in the web page contains magic

In my opinion, the creators of "socket.io" were just a tiny bit too clever in integrating socket.io with node.js, because the way it gets included seems to much like magic.  In "chatty.jade" socket.io is included with this single line:

  script(src="/socket.io/socket.io.js")

which jade turns into this line in the client html file:

  <script src="/socket.io/socket.io.js"></script>

But if you look on the server under the directory where you expect javascript files to be loaded, which is in "/public/javascripts", there is no "socket.io.js" file there.  So how does "socket.io.js" get loaded in the client if the file doesn't exist on the server? That's where the magic comes in:

When socket.io is initialized on the server (require('socket.io').listen(app) in chatserver.js) the socket.io adds a lot of code, much of which alters the server socket code. Part of that alteration is to specifically manage a request for the file "socket.io/socket.io.js", at which point the server-side socket.io code puts together an appropriate socket.io.js file and delivers that to the client.  Magic!

SUMMARY OF CHANGES

Here's a summary of all the changes made in this version (see Getting the code below for full source):

  • add chatserver.js (plagiarized) to use socket.io, and initialize from apps.js
  • create chatty.jade and chatty.css (plagiarized) to be a page for the chatty client, and make express render aware of it through changes to routes/index.js and app.js
  • change layout.js to support block in templates, and alter views/index.jade and apps.js to turn that on

Test and Deploy:

If you run this new code locally (# foreman start) and check http://localhost:3000/, or push this code to the live server (commit with git, then # heroku restart web.1), you should see a new chat window looking much like this:


Now you have a chat-window, live on the web, that anyone can join at any time (from any browser?).  Whoopee!

Getting the code

Today's code is labeled v0001.0003 and is available in these formats:
HelloSun: A guide to creating a social networking empire, from scratch, for free, circa 2012.
⇐ Step 3 T.O.C. Step 5 ⇒

No comments:

Post a Comment