Tuesday, September 13, 2011

Using Amazon Web Services to make a Node.js Web Sockets Server to Play With

When I was at IGN, one of the presentations we saw included a brief introduction to Node.js and web sockets. After that presentation I couldn't wait to try it out, and I started looking into it just a few days after I got home.

After a lot of google searches and trial-and-error, I was able to get Node.js running with a simple web sockets demo (the obligatory chat room demo) on a virtual private server using the free tier of Amazon Web Services. Now I'm looking forward to all the neat things I'll be able to do, but first I'd like to share how I got my server set up so that you can play too. Disclaimer: some of the things I do, like the liberal use of sudo commands, would probably not be the best approach for a server with anything important on it. Since I'm starting with a virtual server with almost nothing on it and I'm just creating it to play with, these steps are okay for my purposes. Use your discretion.

The first thing you'll need to do is set up an AWS (Amazon Web Services) account. Amazon has a pretty nifty tutorial for that here: http://docs.amazonwebservices.com/AWSEC2/latest/GettingStartedGuide/

You'll want to go through the steps of signing up for EC2, launching an instance, and connecting to it via SSH. Important: When you set up your instance, you want to use one of the Basic Amazon Linux AMIs, and you want to create a "micro" instance. That's what included in the free tier; you'll get charged if you do pretty much anything else. For more info on the free tier you can look here. Also if you're paranoid like me you can frequently check your account activity (under the "account" tab) to make sure you're not being charged for anything.

Once you can access your instance, you'll want to enter this:
$sudo yum groupinstall "Development Tools"
$sudo yum install openssl-devel

The Basic Amazon Linux setup is pretty bare-bones, so you run these commands to install a few things, like git and a c++ compiler, and then the SSL thing, which Node.js needs.

Next you'll go through the steps of installing Node.js:
$git clone git://github.com/joyent/node.git
$cd node
$git checkout v0.4.11
$export JOBS=2 # optional, sets number of parallel commands.
$mkdir ~/local
$./configure --prefix=$HOME/local/node
$sudo make #This takes a while
$sudo make install
$echo 'export PATH=$HOME/local/node/bin:$PATH' >> ~/.profile
$echo 'export NODE_PATH=$HOME/local/node:$HOME/local/node/lib/node_modules' >> ~/.profile
$source ~/.profile
Now you have Node.js installed! You can test it out by simply executing$node and then within node enter some javascript to execute. (press ctrl-C to exit)

Now in order to install packages for Node.js you'll need to install npm. The the way I install it (and to get node.js to be able to listen to port 80, as we'll do later) requires that sudo commands be able to find node, which you can ensure by creating the following links:
$sudo ln -s /home/ec2-user/local/node/bin/node /usr/bin/node
$sudo ln -s /home/ec2-user/local/node/lib/node /usr/lib/node
$sudo ln -s /home/ec2-user/local/node/bin/npm /usr/bin/npm
$sudo ln -s /home/ec2-user/local/node/bin/node-waf /usr/bin/node-waf
Then to install npm you simply execute:
$curl http://npmjs.org/install.sh | sudo sh
Once you have npm installed you'll want to use it to install node-static to serve up static content, and socket.io to use websockets:
$npm install node-static
$npm install socket.io
And now you have all you need!

To test it out you just need to create two files: sockettest.js to run the server, and a 'client' directory in the same place as sockettest.js that contains one file, index.html. I would just use vim to create the files and copy and paste the code from below. Here's the code I used for those files:

sockettest.js
var static = require('node-static');
var http = require('http');

var clientFiles = new static.Server('./client');

var httpServer = http.createServer(function(request, response) {
    request.addListener('end', function () {
        clientFiles.serve(request, response);
    });
});
httpServer.listen(80);

var io = require('socket.io').listen(httpServer);

io.sockets.on('connection', function(socket){
        socket.on('msgsend', function(data){
                var dataText = data.text;
                io.sockets.emit('msgbroadcast', {text:dataText, name:data.name});
        });
});


index.html

<html>
<body>
Username: <input id="nameentry" type="text"></input></br>
Text: <input id="textentry" type="text"></input><br/>
<button id="sendbutton" type="button">Send</button>
<br/><br/>
<div id="console" style="width: 100%; background-color: #dddddd;">
        <strong>SOCKET TEST CHAT</strong><br/>
</div>
</body>
</html>

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://123.456.789.101');

        var sendButton = document.getElementById("sendbutton");
        var textInput = document.getElementById("textentry");
        var consoleDiv = document.getElementById("console");
        var nameEntry = document.getElementById("nameentry");

        if (sendButton.addEventListener){
        sendButton.addEventListener('click', function(){
                var textToSend = textInput.value;
                var uname = nameEntry.value;
                if (!uname){
                        uname = "anonymous";
                }
                textInput.value = "";
                if (textToSend){
                        socket.emit('msgsend', {text: textToSend, name: uname});
                }
        }, false);
        }else if(sendButton.attachEvent){
        sendButton.attachEvent('onclick', function(){
                var textToSend = textInput.value;
                textInput.value = "";
                var uname = nameEntry.value;
                if (!uname){
                        uname = "anonymous";
                }
                if (textToSend){
                        socket.emit('msgsend', {text: textToSend, name: uname});
                }
        });
        }

        socket.on('msgbroadcast', function(data){
                consoleDiv.innerHTML += "<strong>"+data.name+": </strong>" + data.text + "<br/>";
        });

</script>
Note: Replace the IP address with that for your own instance. It can be found within the public DNS for your instance, just replace the dashes with dots.

To start your chat server, execute:
$sudo node sockettest.js
Then you can navigate to the public DNS for your instance in your browser and test it out! It should look like this:

Username:
Text:


SOCKET TEST CHAT
Try chatting between different browsers and devices. Socket.io includes fall-backs to support just about any browser, so you shouldn't have to worry about it.

And now you have a functioning demo of a web sockets server running Node.js that you can play around with. Have fun!