unity  back to  
get unified

push client demo: overview
In the unity push client demo, we'll look at two related Flash clients that use XMLSocket to communicate with each other in real time. The first--pushAdmin--will send text messages and .swf file urls to the second--pushViewer. pushViewer, in turn, will receive commands from pushAdmin, and display transmitted text or load specified .swf files. Before we examine pushAdmin and pushViewer in detail, you may want to view the demo in action. Try connecting to pushAdmin and pushViewer in separate windows, then use pushAdmin to send commands to pushViewer.

All unity clients are fundamentally concerned with one thing: sending XML to unity and receiving XML from unity. In order to ease this process, we'll use the unityCore classes to handle the labour of sending and receiving data over sockets. unityCore is not required to build unity clients, it just provides convenient access to XMLSocket communication and reduces the labour required to write new unity clients in Flash.

download
The files discussed in this tutorial are available here. That archive may not contain (and may not be compatible with) the latest release of unityCore.

xml conventions
Though unity itself does not require the use of any specific XML nomenclature, the unityCore client module defines a simple XML convention to use when communicating with unity. Each tag sent to and received from unity takes the general form:

<MESSAGE TYPE="someMethodName" FROM="someOrigin">
  <SUPPORT_TAG(s)>tags_and_data</SUPPORT_TAG(s)>
</MESSAGE>

Borrowing some of the concepts of XML-RPC, the MESSAGE tag is normally used to trigger a remote method specified by the TYPE attribute. The FROM attribute indicates where the message originated. Inside the MESSAGE tag are any number of arbitrary support tags; these are passed to the specified method as a parameter.

Here is a specific example of a MESSAGE tag sent by a client to unity:

<MESSAGE TYPE="requestRoom" FROM="pushAdmin">
  <ROOMID>pushAdmin</ROOMID>
</MESSAGE>

When a client connects to unity, it is placed in a generic dispatcher room and expected to ask to enter a more meaningful room using the above requestRoom message. The requestRoom message is the only tag actually required by the base unity server. All other messages are application defined. To create the push demo clients, we'll continue to use the MESSAGE tag convention, but it's important to note that any XML can be used, including more fully developed schemas like RPC. Custom applications, of course, require custom XML implementations on the server side (new unity rooms).

unityCore classes
We'll use the classes in the unityCore module to get our clients up and running quickly. The three unityCore classes hide lower level socket communication and provide logging services, as outlined below. (For the curious, documentation describing the inner functioning of the core classes is available via code comments in the classes' source.)

Class Description Public Methods
SocketManager() Abstract class (must be subclassed). Provides socket connection, disconnection, and data transfer services.
doConnect() -connect to server
setServer() -set server host/port
killConnect() -disconnect from server
requestRoom() -ask server to join a room
setMsgProcessor() -sets the MessageProcessor to use when XML arrives.
onConnectFailure() -when connect attempt fails (implement in subclass)
onConnectSuccess() -when connect attempt succeeds (implement in subclass)
onServerKillConnect() -when server kills connection (implement in subclass)
onClientKillConnect() -when flash kills connection (implement in subclass)
MessageProcessor() Abstract class (must be subclassed). Invokes methods based on incoming XML MESSAGE objects and passes outgoing MESSAGEs to the socket manager for transfer.
receiveMessage() -invokes method specified by incoming XML MESSAGE
sendMessage() -Sends a message through SocketManager to unity.
setSockMan() -Specifies the SocketManager to use for sending/receiving XML.
Log() Sends log entries to log movie clip and Flash Output window.
addEntry() -Outputs text to log movie clip and Output window.
hide() -Makes the log movie clip invisible.
show() -Makes the log movie clip visible.

pushViewer walkthrough
Now that we're familiar with unityCore and the XML conventions used in unity clients, let's see how the pushViewer client works. Recall the pushViewer's functions:

We'll start with the messages pushViewer manages before moving on to the classes that make pushViewer work.

pushViewer MESSAGE tags - sending
The pushViewer only sends one MESSAGE tag to the server--the requestRoom tag we discussed earlier:

<MESSAGE TYPE="requestRoom" FROM="admin">
  <ROOMID>PushViewer</ROOMID>
</MESSAGE>

The ROOMID tag is set to "PushViewer", so unity will place our client in the PushViewer room when we send this requestRoom message. The ROOMID is an arbitrary string that simply identifies the room to the server.

pushViewer MESSAGE tags - receiving
The pushViewer can receive and respond to four MESSAGE tags, as follows:

<MESSAGE TYPE="numUsers" FROM="unity">
  <NUMBER>n</NUMBER>
</MESSAGE>

The numUsers message tells our client how many other PushViewer clients are connected (specified by the NUMBER tag).

<MESSAGE TYPE="broadcastText" FROM="pushAdmin">
  <TEXT>Hello world...</TEXT>
</MESSAGE>
The broadcastText message causes PushViewer to display text sent from PushAdmin. The text to display is contained by the TEXT tag.

<MESSAGE TYPE="loadMovie" FROM="pushAdmin">
  <FILEPATH>myMovie.swf</FILEPATH>
</MESSAGE>

The loadMovie message causes PushViewer to display the .swf file specified by the FILEPATH tag in a container movie clip.

<MESSAGE TYPE="eventNotice" FROM="pushAdmin">
  <EVENT>reconnect</EVENT>
</MESSAGE>

Finally, the eventNotice message simply triggers the method specified in the EVENT tag. eventNotice provides a generic means of executing any method (without parameters) in a unity client without requiring server-side intervention. In this case, we use eventNotice to call reconnect(), which causes the client to disconnect and connect again (used for load testing).

extending the unityCore classes
With our MESSAGE tags defined, we now have to extend the SocketManager and MessageProcessor classes so we can connect to unity and send and receive XML. Our subclasses will be called PushViewerSocketManager and PushViewerMessageProcessor.

the PushViewerSocketManager class
Here's the source of PushViewerSocketManager (stored as a separate .as file in the pushDemo.zip archive):

/************************************************************/
/*
 * PushViewerSocketManager Class. Extends SocketManager.
 *   Version: 0.3.0
 *   Desc: Implements specific repsonses to generic socket 
 *         connection events.
 */
 
// ===============================
// Set superclass to SocketManager
// ===============================
PushViewerSocketManager.prototype = new Object.pvMain.SocketManager();

// =================
// Class constructor
// =================
function PushViewerSocketManager (host, port, log) {
  // Invoke super's constructor.
  this.superclass = Object.pvMain.SocketManager;
  this.superclass(host, port, log);
}

// ================
// Instance Methods
// ================
PushViewerSocketManager.prototype.onConnectFailure = function () {
  Object.pvMain.gotoAndStop("connectionFailed");
}

PushViewerSocketManager.prototype.onConnectSuccess = function () {
  this.requestRoom("pushViewer", "pushViewer");
  Object.pvMain.gotoAndStop("clientInterface");
}

PushViewerSocketManager.prototype.onServerKillConnect = function () {
  Object.pvMain.gotoAndStop("connectionFailed");
}  

PushViewerSocketManager.prototype.onClientKillConnect = function () {
  Object.pvMain.gotoAndStop("connectionFailed");
}  
/************************************************************/


The PushViewerSocketManager class's first task is to set up its superclass, SocketManager, like this:

PushViewerSocketManager.prototype = new Object.pvMain.SocketManager();

The SocketManager class lives on the _root of the pushViewer.swf file, but we refer to the _root as Object.pvMain. Each client sets a property of Object corresponding to its _root timeline. This keeps our code portable so the entire pushViewer client could be loaded into another movie.

The PushViewerSocketManager constructor takes three arguments (which it passes to its superclass's constructor):
The PushViewerSocketManager methods provide our client's response to various connection events triggered by the SocketManager superclass. These callbacks define our our application's base network functionality: what to do when a new connection attempt succeeds or fails, and how to handle losing an established connection.

In the pushViewer, we use the same "connectionFailed" frame to respond visually to all connection problems. Hence, all three connection failure methods (onConnectFailure(), onServerKillConnect(), and onClientKillConnect()) contain a single line of code:

Object.pvMain.gotoAndStop("connectionFailed");

That line simply tells the main timeline of the pushViewer (aka Ojbect.pvMain) to goto the "connectionFailed" frame (see /pushViewer/pushViewer.fla in the pushDemo.zip file).

Our onConnectSuccess() method is fired when a connection attempt with the server completes sucessfully. We use it to:
  1. ask unity to place the client in the pushViewer room (we do this with the requestRoom() method, which takes two arguments, the roomID, and the origin of the request)
  2. move the user from the "login" frame, which displays a please wait message, to the "clientInterface" frame, which provides an interface for text messages and loaded movies.
Any new unity client, hence, can get a server connection running in two short steps:
  1. Subclass the unityCore SocketManager class.
  2. Provide implementations for the methods: onConnectFailure(), onConnectSuccess(), onServerKillConnect(), and onClientKillConnect().

the PushViewerMessageProcessor class
The PushViewerMessageProcessor class provides methods that are called in response to incoming XML MESSAGE tags. Its superclass, MessageProcessor, handles the XML itself; PushViewerMessageProcessor need only implement the appropriate methods. Earlier we learned that pushViewer could respond to four MESSAGE tag types: numUsers, broadcastText, loadMovie, and eventNotice. In the source for PushViewerMessageProcessor below, we see these methods implemented:


/************************************************************/
/*
 * PushViewerMessageProcessor Class. Extends MessageProcessor.
 *   Version: 0.3.0
 *   Desc: Responds to the receipt of various message types sent 
 *         by the server to the client. Each method deals with one
 *         type of message.
 */
 
// ==================================
// Set superclass to MessageProcessor
// ==================================
PushViewerMessageProcessor.prototype = new Object.pvMain.MessageProcessor();

// =================
// Class constructor
// ================= 
function PushViewerMessageProcessor (log) {
  // Pass our arguments on to the super class constructor.
  this.superclass = Object.pvMain.MessageProcessor;
  this.superclass(log);
}

// ================
// Instance Methods
// ================

/*
 * Handles ALL generic eventNotice MESSAGESs broadcast by server.
 * Invokes the method on the PushViewerMesssageProcessor that corresponds
 * to the EVENT tag in the MESSAGE.
 */
PushViewerMessageProcessor.prototype.eventNotice = function (msg) {
  // If the method exists...
  if (typeof this[msg[0].firstChild.nodeValue] == "function") {
    // ...invoke it.
    this[msg[0].firstChild.nodeValue]();
  } else {
    // ...otherwise, generate an error message
    this.log.addEntry("Event could not fire. No such method: " 
                       + msg[0].firstChild.nodeValue);
  }
}

/*
 * Handles load movie commands sent by server.
 */
PushViewerMessageProcessor.prototype.loadMovie = function (msg) {
  Object.pvMain.movieViewer.viewClip.loadMovie(msg[0].firstChild.nodeValue);
  Object.pvMain.broadcastTextOutput = "loading new movie...";  
}

/*
 * Handles text messages sent by server.
 */
PushViewerMessageProcessor.prototype.broadcastText = function (msg) {
  Object.pvMain.broadcastTextOutput = msg[0].firstChild.nodeValue;
}

/*
 * Handles reconnect eventNotices.
 */
PushViewerMessageProcessor.prototype.reconnect = function (msg) {  
   Object.pvMain.gotoAndPlay("reconnect");
}

/*
 * Displays number of connected users.
 */
PushViewerMessageProcessor.prototype.numUsers = function (msg) {
  // Retrieve and format the number.
  var numUsers = msg[0].firstChild.nodeValue;
  var numChars = numUsers.length;
  var extraZeros = 4 - numChars > 0 ? 4 - numChars : 0;
  var zeroString = "";
  for (var i = 0; i < extraZeros; i++) {
    zeroString += "0";
  }
  
  // Output the number to screen.
  numUsersOutput = zeroString + numUsers;
}

/************************************************************/
The methods of PushViewerMessageProcessor are generally simple. The broadcastText() method, for example, does only one thing: sets the value of an onscreen text field. The loadMovie() method loads a movie into a movie clip container on the pushViewer's main timeline. The methods dictate what should happen when the corresponding MESSAGE tag arrives.

Each method is passed a single parameter, msg, which contains the array of child nodes (tags) descending from the MESSAGE tag. Each method uses the information in these child nodes to perform its task.

One method bears special attention: eventNotice(). It extracts the text contained by the EVENT tag and calls the method of that name, without any parameters. In the line:

this[msg[0].firstChild.nodeValue]();

the this refers to the PushViewerMessageProcessor object and msg[0].firstChild.nodeValue is the EVENT tag's text. We use the [] operator to access the property specified by the EVENT tag's text, and we invoke it using ().

So, in general, any new client can start responding to MESSAGEs in two steps:
  1. Subclass the unityCore MessageProcessor class.
  2. Provide an implementation for each method named by a MESSAGE tag's TYPE attribute.
Note that the MessageProcessor class doesn't just receive messages from unity; it als sends them to it. The sendMessage() method is inherited by our PushViewerMessageProcessor class, so we could send messages to unity like this:

msgProcessor.sendMessage(XMLmessage);

Our PushViewerMessageProcessor doesn't actually send any messages to unity. The only message sent to unity from pushViewer is requestRoom, and that's sent by the SocketManager class directly. We'll learn more about sending messages when we study pushAdmin.

using the pushViewer classes (pushViewer.fla)
Now that we know how the pushViewer classes work, let's see how to use them in our flash .fla file, pushViewer.fla. After some preloading, pushViewer.fla runs the code on the frame labelled "main", the first half of which is listed below. Read through the comments to learn how the classes are imported and initialized.

/************************************************************/

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// unity pushViewer
// version 0.9.0
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

// First, we set a property of Object to the main timeline. All
// our classes use this instead of _root so we can safely load the whole
// pushViewer.swf file into another movie.
Object.pvMain = this;

// Now we load the general socket communication classes of unityCore.
// This just brings in the source code...we haven't instantiated
// anything yet.
#include "../unityCore/SocketManager.as"
#include "../unityCore/MessageProcessor.as"

// Here we load the push viewer subclasses we studied earlier. Again,
// this is just a load, no objects have been made yet.
#include "PushViewerSocketManager.as"
#include "PushViewerMessageProcessor.as"

// Now we actually instantiate our socket communication tools,
// PushViewerSocketManager and PushViewerMessageProcessor and store
// the objects in sockMan and msgProcessor. Initially, we set
// the sockMan server and port to "" and 0 respectively. We'll set
// that information later once we load it in from the config file.
// The null value passed to both constructors is the log...we aren't
// logging in the client, so there's no Log object to pass.
sockMan = new PushViewerSocketManager("", 0, null);
msgProcessor = new PushViewerMessageProcessor(null);

// We have made our two socket communication objects, but they don't
// know about each other. Our last task is to provide each object with
// a reference to the other. They use the references when passing
// information back and forth.
sockMan.setMsgProcessor(msgProcessor);
msgProcessor.setSockMan(sockMan);

/************************************************************/
Once our objects are created and initialized, our next task is normally to set a server and port (if one wasn't set by the constructor) and then connect, as follows:
// Tell the socket manager which server we want to use.
sockMan.setServer("someserver.com", 2001);

// Attempt to open socket...
sockMan.doConnect();
The results of doConnect() are all handled by the callbacks of sockMan (an instance of PushViewerSocketManager). No other code is required to connect when the unityCore subclasses are in place. In the specific case of pushViewer, the server to which to connect is actually loaded from an external XML configuration file (pushViewerConfig.xml). The doConnect() call comes after the config file loads (see the pushViewer.fla file).

If the connection attempt is successful, sockMan.onConnectSuccess() fires and the movie displays the "clientInterface" frame. From that point forward, all messages received by sockMan are passed to msgProcessor, which in turn executes methods specified by the XML MESSAGE tag's TYPE attribute.

pushAdmin walkthrough
Recall that pushAdmin sends commands to pushViewer, causing pushViewer to display text and movies specified by pushAdmin. The pushAdmin client works almost exactly like the pushViewer--it:

Much of the pushAdmin code, hence, varies only slightly from the pushViewer code. Rather than rehash what we learned with pushViewer, we'll focus on the aspects of the pushAdmin that are new. For further study, you can examine the following source files in the unityPush.zip archive, which will look very similar to their pushViewer counterparts: The two .as files are the subclasses of the unityCore. The pushAdmin client only receives one MESSAGE tag from unity, so its PushAdminMessageProcessor implements only a single method.

The PushAdminXMLMessages.txt file is a list of XML MESSAGES used by pushAdmin. Note that the MESSAGES are not different from those used by pushViewer; they are simply rearranged to give a complete picture of which tags are sent by the pushAdmin and which are received by it.

the PushAdminMessageBuilder class
Because pushAdmin sends a variety of commands to pushViewer, it requires a class pushViewer did not: PushAdminMessageBuilder. This class assembles XML MESSAGE tags to be transferred to unity.

Here's the source for PushAdminMessageBuilder:

/************************************************************/
/*
 * PushAdminMessageBuilder Class
 *   Version: 0.2.0
 *   Desc: Creates XML fragment messages to be sent to the server.
 *         Each method builds one type of message.
 */

// =================
// Class constructor
// ================= 
function PushAdminMessageBuilder () {
  // Default constructor. No initialization required.
}

// ================
// Instance Methods
// ================
// All methods create tags to be sent to the server from the admin.
// See xml structure files for documentation on each tag.

/*
 * Broadcast methods
 */
// Makes a broadcastText tag
PushAdminMessageBuilder.prototype.createBroadcastText = function (text) {
  var msg = ''
            + '' + text + ''
            + '';
  return msg;
  trace("made bt: " + text);
}

// Makes a loadMovie tag
PushAdminMessageBuilder.prototype.createLoadMovie = function (fileName) {
  var msg = ''
            + '' + fileName + ''
            + '';
  return msg;
}

// Makes an eventNotice tag
PushAdminMessageBuilder.prototype.createEventNotice = function (eventName) {
  var msg = ''
            + '' + eventName + ''
            + '';
  return msg;
}

/************************************************************/
For each type of MESSAGE tag that pushAdmin sends to unity, the PushAdminMessageBuilder class provides a corresponding method. For example, to generate a MESSAGE of TYPE "broadcastText", we invoke createBroadcastText(), and pass it the text we wish to broadcast. The createBroadcastText() method wraps the text in an appropriate XML MESSAGE tag and returns that tag as a string. Note that there's no need to parse the MESSAGE string into an XML object--by passing XMLSocket.send() a string we avoid the cost of generating an XML object.

sending messages to unity from pushAdmin
Once the PushAdminMessageProcessor and PushAdminMessageBuilder objects are instantiated, sending messages to unity is trivial. We call the sendMessage() method of the PushAdminMessageProcessor object (msgProcessor), and pass it the MESSAGE tag to send:

msgProcessor.sendMessage(MESSAGE_TAG_GOES_HERE);

Of course, we're building our MESSAGEs with our handy PushAdminMessageBuilder object, so here's how we actually send three types of messages to unity from pushAdmin:

msgProcessor.sendMessage(msgBuilder.createBroadcastText(broadcastTextInput));
msgProcessor.sendMessage(msgBuilder.createLoadMovie(swfFileInput));
msgProcessor.sendMessage(msgBuilder.createEventNotice("reconnect"));


pushAdmin logging
For the sake of simplicity, pushViewer did not include any logging. But we're now familiar with the general unity client system, so let's see how to add a logging feature to pushAdmin.

The Log class comes as part of unityCore. We use it to send messages both to the Flash Output window (Test Movie mode) and to a text field in a custom movie clip. Here's how we create a functioning log:

  1. We create a movie clip called logWindow that contains a text field named logOutput.
  2. Next, we include the Log.as file in our .fla:
    #include "../unityCore/Log.as"
  3. Then we make a new Log object (the parameters are: 1) the log clip, and 2) the maximum characters to display in the log text field before clearing it):
    sysLog = new Log(logWindow, 300);
  4. Finally, we pass the log object to the SocketManager and MessageProcessor class constructors:
    sockMan = new PushAdminSocketManager("", 0, sysLog);
    msgProcessor = new PushAdminMessageProcessor(sysLog);
To send a message to the log, the sockMan and msgProcessor use the log's addEntry() method. We can also use addEntry() whenever we want to send a message to the log. For example:
sysLog.addEntry("this is a test");

To hide and show the logWindow clip, we use the log's hide() and show() methods.

conclusion
The pushDemo has showed how a specific implementation of a unity client works. To view an abstracted list of steps required to produce a client using unityCore, see: the shortest route to a new unity client

revision history
december 12, 2001: posted.