Node.js for Flash & Flex Introduction

While researching technologies for a new Flash multiplayer gaming project, I came across Node.js as a backend techology. I’ve heard of it for a while, but have not yet used it. Its nice.

Node.js is a server software that allows you to a) run JavaScript on the server and b) talk to/from the client-side using http and socket connections. While not designed with Flash in mind, you can easily connect Flash (or Flex) to the socket connection and get real-time data flowing to/from. Very nice.

Uses for Node.js with Flash

What I think is immediately attractive is that you can easily setup a backend for your games. Its running in JavaScript which is easy to use and learn and the write-compile-test cycle couldn’t be faster. You could feed dynamic data to your game such as levels in a level based game. You could feed configuration information in so that you can easily edit it without editing your Flash project. You can create real-time data feeds or multiplayer gaming.

Note: Node.js is light-weight. You’ll have to code pretty much anything (multiplayer gaming) you want to add to it. It is a great starting point though.

Tutorial

I created the following example, and this post within an hour. Within 5 minutes you can setup your own running version of the demo shown in the post. Follow along here or simply download the full source code (See ‘Member Resources’ below) and check it out.

Note: I’m running the node.js server and the Flash swf on my local computer. I imagine this is where I would develop the full game. Its fast and easy to run/edit/run. Then I’d migrate the project to a server. I *think* its trivial to get this running up on a webserver for your users to enjoy, but I have not done that yet.

A. Installing Node.js (on Mac)

  1. Download Node.js
  2. Run the installer
  3. You now use the Mac OSX Terminal program on Mac (or Find any ssh terminal emulator program for Windows) to run Node. We’ll do that farther below.
  4. Done!

B. Running the server on Node.js (via Socket)

  1. Create a new text file HelloWorldNodeJS.js and save (anywhere?) on your local machine
  2. Edit the file in your favorite text editor. For now, just enter the code below this list into that file and save.
  3. Open Terminal and run one line –  ‘node /path/to/your/dir/HelloWorldNodeJS.js’
  4. Done!

[actionscript3]

// ————————————–
// Imports
// ————————————–
var net = require(‘net’);
var mySocket;

// ————————————–
// Construct The Socket
// ————————————–
// create the server and register event listeners
var server = net.createServer(function(socket) {
mySocket = socket;
mySocket.on("connect", onConnect);
mySocket.on("data", onData);
});

// ————————————–
// Events
// ————————————–
/**
* Handles the Event: <code>"connect"</code>.
*
*/
function onConnect()
{
console.log("Connected to Flash");
}

/**
* Handles the Event: <code>"data"</code>.
*
* When flash sends us data, this method will handle it
*
*/
function onData(d)
{
if(d == "exit\0")
{
console.log("exit");
mySocket.end();
server.close();
}
else
{
console.log("From Flash = " + d);
mySocket.write(d, ‘utf8’);
}
}

// ————————————–
// Start the Socket
// ————————————–
server.listen(9001, "127.0.0.1");

[/actionscript3]

C. Stopping the server on Node.js (via Socket)

Once you run B.3 above, the server runs and stays running unless you program your client or server to kill the connection. If you try to run B.3 without killing the connection you’ll see an error in Terminal (on Mac) containing “Error: listen EADDRINUSE”. To manually kill the connection (this is required if you want to edit and run B.3 again) then run the following sequence.

  1. Open Terminal (on Mac) and run ‘ps aux | grep node’
  2. This will show a series of lines like this ‘srivello       PID   0.0  0.1  3064476   7152 s002  T     4:41PM   0:00.12 node HelloWorldNodeJS.js’.
  3. Where you see ‘PID’ that is the PID. Your PID will be different it will be a #.
  4. Then run ‘kill -9 PID’. Replace PID with your #.

D. Running the client in AS3 (via Socket)

  1. Create a new Flash project using your favorite IDE. My example uses Flash Builder 4.6.x.
  2. Edit your main document class. For now simply replace the class with the code below this list and save.
  3. Compile and run the code. See your output window for the exciting details.
  4. Done!

[actionscript3]

package
{

// ————————————–
// Imports
// ————————————–
import flash.display.Sprite;
import flash.events.DataEvent;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.net.XMLSocket;
import flash.text.TextField;
import flash.ui.Keyboard;

// ————————————–
// Class
// ————————————–
/**
* <p>The <code>NodeJSDemo</code> class is …</p>
*
*/
public class NodeJSDemo extends Sprite
{

// ————————————–
// Properties
// ————————————–
// PUBLIC GETTER/SETTERS
/**
* The core object which connects client to
* server for real-time communication
*/
private var _xmlSocket:XMLSocket;

/**
* The sole UI element to show some test output
*/
private var _textField:TextField;

// PUBLIC CONST

// PRIVATE

// ————————————–
// Constructor
// ————————————–
/**
* This is the constructor.
*
*/
public function NodeJSDemo()
{
// SUPER
super();

// EVENTS
addEventListener(Event.ADDED_TO_STAGE, _onAddedToStage);

// VARIABLES

// PROPERTIES
_textField = new TextField ();
_textField.width = 300;
_textField.height = 300;
_textField.multiline = true;
_textField.htmlText = "";
addChild(_textField);

// METHODS

}

// ————————————–
// Methods
// ————————————–
// PUBLIC
/**
* Handles the Event: <code>Event.CHANGE</code>.
*
* @param aEvent <code>Event</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onAddedToStage(aEvent : Event):void
{
// CREATE THE SOCKET AND CONNECT – I’M NOT SURE WHAT PARAMETERS ARE VALID HERE
// BUT IT MUST MATCH THE SETTINGS WITHIN THE ‘HelloWorldNodeJS.js’ FILE YOU ARE USING
_xmlSocket = new XMLSocket("127.0.0.1", 9001);

// EVENTS
stage.removeEventListener(MouseEvent.CLICK, _onAddedToStage);
//
_xmlSocket.addEventListener(Event.CONNECT, _onConnected);
_xmlSocket.addEventListener(IOErrorEvent.IO_ERROR, _onIOError);

}

// PRIVATE

// ————————————–
// Event Handlers
// ————————————–
/**
* Handles the Event: <code>Event.CHANGE</code>.
*
* @param aEvent <code>Event</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onConnected(aEvent : Event):void
{
// TRACE
trace("onConnect() aEvent: " + aEvent);

// EVENTS
_xmlSocket.removeEventListener(Event.CONNECT, _onConnected);
_xmlSocket.removeEventListener(IOErrorEvent.IO_ERROR, _onIOError);
//
_xmlSocket.addEventListener(DataEvent.DATA, _onDataReceived);
_xmlSocket.addEventListener(Event.CLOSE, _onSocketClose);
//
stage.addEventListener(KeyboardEvent.KEY_UP, _onKeyUp);

}

/**
* Handles the Event: <code>Event.CHANGE</code>.
*
* @param aEvent <code>Event</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onSocketClose(aEvent : Event):void
{
// TRACE
trace("_onSocketClose aEvent : " + aEvent);

// EVENTS
stage.removeEventListener(KeyboardEvent.KEY_UP, _onKeyUp);
_xmlSocket.removeEventListener(Event.CLOSE, _onSocketClose);
_xmlSocket.removeEventListener(DataEvent.DATA, _onDataReceived);
}

/**
* Handles the Event: <code>KeyboardEvent.KEY_UP</code>.
*
* @param aEvent <code>KeyboardEvent</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onKeyUp(evt:KeyboardEvent):void
{
// HANDLE KEYS
if (evt.keyCode == Keyboard.ESCAPE) {
//1. SEND ‘KILL THE SERVER’ TO THE SERVER
_xmlSocket.send("exit");
}
else {
//2. SEND ANY NON-ESCAPE KEY’S CODE (’38’ FOR EXAMPLE) TO SERVER
// THIS IS A SILLY EXAMPLE OF SENDING A NUMBER TO THE SERVER
// JUST SO THE SERVER CAN SEND IT BACK. SIMPLE.
_xmlSocket.send(evt.keyCode);
}
}

/**
* Handles the Event: <code>DataEvent.DATA</code>.
*
* @param aEvent <code>DataEvent</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onDataReceived(aEvent : DataEvent):void
{
try {

// Show the server data in text
_textField.htmlText += ("From Server: " + aEvent.data + "\n");

//scroll down to show latest line
_textField.scrollV = _textField.maxScrollV;

} catch (error : Error) {
// TRACE
trace("_onDataReceived error: " + error);
}
}

/**
* Handles the Event: <code>IOErrorEvent.IO_ERROR</code>.
*
* @param aEvent <code>IOErrorEvent</code> The incoming aEvent payload.
*
* @return void
*
*/
private function _onIOError(aEvent : IOErrorEvent):void
{
// TRACE
trace("_onIOError aEvent: " + aEvent);

// EVENTS
_xmlSocket.removeEventListener(Event.CONNECT, _onConnected);
_xmlSocket.removeEventListener(IOErrorEvent.IO_ERROR, _onIOError);
stage.addEventListener(MouseEvent.CLICK, _onAddedToStage);
}

}
}

[/actionscript3]

E. Updating your client/server

  1. Revisit steps B.2 and B.3 for server
  2. Revisit steps C.2, D.2 and D.3 for client
  3. Done!

Pretty awesome.

Next Steps

  • Read an overview of What is Node.js?
  • Checkout the official homepage for Node.js
  • Download the full source code (See ‘Member Resources’ below).

Member Resources

[private_Free member]Enjoy this members-only content!

[/private_Free member]