More Books
Flash Communication Server
Flash Communication Server
Table of Contents
Copyright
About the Authors
Brian Lesser
Giacomo
Joey Lott
Robert Reinhardt
Justin Watkins
Foreword
Preface
What Does FlashCom Offer?
What's in This Book?
How to Use This Book
Audience
ActionScript 1.0 Versus ActionScript 2.0
Server-Side ActionScript
The flash-communications.net Site
Director, Breeze, and Other Options
Flash Video Options
Licensing and Hosting Options
Conventions Used in This Book
Voice
Using Code Examples
Safari Enabled
Comments and Questions
Acknowledgments
Part I:  FlashCom Foundation
Chapter 1.  Introducing the Flash Communication Server
Section 1.1.  Clients and Servers
Section 1.2.  Creating an Application
Section 1.3.  Real-Time Messaging Protocol
Section 1.4.  The Communication Classes
Section 1.5.  Communicating with Application Servers, Databases, and Directory Servers
Section 1.6.  Firewalls and Security
Section 1.7.  Getting Started
Section 1.8.  Hello Video!
Section 1.9.  Conclusion
Chapter 2.  Communication Components
Section 2.1.  Overview of Communication Components
Section 2.2.  Summary of Communication Components
Section 2.3.  Creating an Application that Monitorsa Connection
Section 2.4.  Building a Simple Chat Room
Section 2.5.  Adding Audio and Video to the Chat Room
Section 2.6.  Forgoing the SimpleConnect Component
Section 2.7.  Conclusion
Chapter 3.  Managing Connections
Section 3.1.  Making a Connection
Section 3.2.  Managing a Connection
Section 3.3.  Reusing a NetConnection Object
Section 3.4.  Multiple Simultaneous NetConnection Objects
Section 3.5.  Testing and Debugging Network Connections
Section 3.6.  Subclassing the NetConnection Class
Section 3.7.  Communication Components Without SimpleConnect
Section 3.8.  Conclusion
Chapter 4.  Applications, Instances, and Server-Side ActionScript
Section 4.1.  Scripting Application Instances
Section 4.2.  Differences Between Flash ActionScript and Server-Side ActionScript
Section 4.3.  The Life of an Application Instance
Section 4.4.  Running a Simple Hello World Test Script
Section 4.5.  A More Realistic Example
Section 4.6.  Instance-to-Instance Communications
Section 4.7.  Script Filenames and Locations in Detail
Section 4.8.  Testing and Debugging Server-SideScript Files
Section 4.9.  Designing Communication Applications
Section 4.10.  Conclusion
Part II:  Audio, Video, and Data Streams
Chapter 5.  Managing Streams
Section 5.1.  A Simple Publisher/Subscriber Example
Section 5.2.  Stream Names
Section 5.3.  Publishing Streams in Detail
Section 5.4.  Playing Streams in Detail
Section 5.5.  The Stream Class
Section 5.6.  Publishing and Playing ActionScript Data
Section 5.7.  Creating Synchronized Presentations
Section 5.8.  The NetStream and Stream Information Objects
Section 5.9.  Stream Enhancements and Limitations
Section 5.10.  Conclusion
Chapter 6.  Microphone and Camera
Section 6.1.  Working with Microphone/Audio Input
Section 6.2.  Working with Camera Input
Section 6.3.  Building a Message-Taking Application
Section 6.4.  Building a Surveillance Application
Section 6.5.  Conclusion
Chapter 7.  Media Preparation and Delivery
Section 7.1.  Audio and Video Compression
Section 7.2.  Converting Prerecorded Materialto FLV Format
Section 7.3.  Using Flash Pro's Media Components
Section 7.4.  Enabling Multiple Bit Rate FLVsWithin an Application
Section 7.5.  Streaming MP3 Audio
Section 7.6.  Conclusion
Part III:  Remote Connectivity and Communication
Chapter 8.  Shared Objects
Section 8.1.  Objects and Shared Objects
Section 8.2.  Getting a Shared Object in Flash
Section 8.3.  Updates and Frame Rates
Section 8.4.  Scripting Shared Objects on the Server
Section 8.5.  Temporary and Persistent Shared Objects
Section 8.6.  Proxied Shared Objects
Section 8.7.  Shared Objects and Custom Classes
Section 8.8.  Avoiding Collisions
Section 8.9.  Optimizing Shared Object Performance
Section 8.10.  Broadcasting Remote Method Callswith send( )
Section 8.11.  A Simple Video and Text Chat Application
Section 8.12.  Conclusion
Chapter 9.  Remote Methods
Section 9.1.  Why Use Calls?
Section 9.2.  The send( ) and call( ) Methods
Section 9.3.  Client-to-Server Calls
Section 9.4.  Server-to-Client Calls
Section 9.5.  Server-to-Server Calls
Section 9.6.  A Simple Lobby/Rooms Application
Section 9.7.  Debugging Calls
Section 9.8.  Advanced Topics
Section 9.9.  Conclusion
Chapter 10.  Server Management API
Section 10.1.  Connecting to the Admin Service
Section 10.2.  Using the Server Management API
Section 10.3.  Server Management API Uses
Section 10.4.  Conclusion
Chapter 11.  Flash Remoting
Section 11.1.  The Remoting Gateway
Section 11.2.  Remoting Basics
Section 11.3.  Role of Remoting in FlashCom Applications
Section 11.4.  Securing Access
Section 11.5.  Conclusion
Chapter 12.  ColdFusion MX and FlashCom
Section 12.1.  Understanding ColdFusion MXand Flash Remoting
Section 12.2.  Using Flash Remoting to Log Events
Section 12.3.  Getting a List of Streams
Section 12.4.  Using ColdFusion and FTP to Mirror Streams
Section 12.5.  Conclusion
Part IV:  Design and Deployment
Chapter 13.  Building Communication Components
Section 13.1.  Source Files
Section 13.2.  People Lists
Section 13.3.  A Simple People List
Section 13.4.  Listenable Shared Objects
Section 13.5.  Status and People List
Section 13.6.  Text Chat
Section 13.7.  Shared Text
Section 13.8.  Video Conference and Video Window
Section 13.9.  PeopleGrid
Section 13.10.  Summary
Section 13.11.  Conclusion
Chapter 14.  Understanding the Macromedia Component Framework
Section 14.1.  The Component Framework
Section 14.2.  Under the Hood of the Chat Component
Section 14.3.  Creating a Simple Component from Scratch: SharedTextInput
Section 14.4.  Creating a Container Component: SharedAddressForm
Section 14.5.  Creating an Authenticating Component
Section 14.6.  Integrating Components with Your Existing Applications
Section 14.7.  Understanding the Framework
Section 14.8.  Conclusion
Chapter 15.  Application Design Patterns and Best Practices
Section 15.1.  Shared Object Management
Section 15.2.  Moving Code to the Server
Section 15.3.  Building Façades on the Server
Section 15.4.  Server-Side Client Queues
Section 15.5.  A Framework for Recording and Playing Back Componentized Applications
Section 15.6.  Components and Component Frameworks
Section 15.7.  Conclusion
Chapter 16.  Building Scalable Applications
Section 16.1.  Coordinating Instances
Section 16.2.  Scalability and Load Balancing
Section 16.3.  Conclusion
Chapter 17.  Network Performance, Latency,and Concurrency
Section 17.1.  Latency
Section 17.2.  Bandwidth
Section 17.3.  Concurrency
Section 17.4.  Conclusion
Chapter 18.  Securing Applications
Section 18.1.  The Three A's: Authentication, Authorization, and Accounting
Section 18.2.  Authentication
Section 18.3.  Authorization
Section 18.4.  Accounting
Section 18.5.  Suggestions and References
Section 18.6.  Conclusion
Index
SYMBOL
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
R
S
T
U
V
W

8.4. Scripting Shared Objects on the Server

Working with remote shared objects in Server-Side ActionScript is a little different than working with them in the Flash client. Example 8-5 shows a short main.asc file that forces users to sign into an application with a unique username and keeps track of each user in a remote shared object.

Example 8-5. main.asc file for a simple video chat application
// trim(  ) removes whitespace from the beginning and end of a string.
function trim (str) {
  if (typeof str != "string") return "";  // Make sure str is a string.
  str = str.replace(/^\s*/, "");          // Trim leading spaces.
  str = str.replace(/\s*$/, "");          // Trim trailing spaces.
  str = str.replace(/\n/g,  "");          // Remove new lines.
  str = str.replace(/\r/g,  "");          // Remove carriage returns.
  str = str.replace(/\t/g,  "");          // Remove tabs.
  return str;
}

// Get a temporary (non-persistent) RSO for a read-only list of usernames.
application.onAppStart = function ( ) {
   userList_so = SharedObject.get("public/userList");
};

/* Accept a client whose username is a unique, non-empty string
 * and update the userList shared object.
 * Each slot value in userList is the path to which each client
 * has write access and where other clients should look for
 * a stream to which to subscribe.
 */
application.onConnect = function (client, userName) {
  userName = trim(userName);
  if (userName.length == 0) {
    application.rejectConnection(client, {msg: "Blank or missing user name."});
    return;
  }

  var userPath = userList_so.getProperty(userName);
  if (userPath) {
    application.rejectConnection(client,
       {msg: "The user name '" + userName + "' is already in use."});
    return;
  }
  client.readAccess  = "public";
  client.writeAccess = "public/chat";
  client.writeAccess = "public/textchat";
  client.userName = userName;
  userList_so.setProperty(userName, "public/chat/" + userName);

  return true;
};

// Delete records of clients when they disconnect.
application.onDisconnect = function (client) {
   userList_so.setProperty(client.userName, null);
};

The server-side SharedObject.get( ) method is used in place of the client-side SharedObject.getRemote( ) method in Flash and does not require a URI to the application instance (because it is executed from within the FlashCom application):

userList_so = SharedObject.get("public/userList");

Like streams, shared objects are identified by partial URIs that are relative to the application instance. In Example 8-5, the userList shared object is created within the public path. In the application.onConnect( ) method, all clients are given read-only access to the public path so that they can read but not update the userList shared object.

Since the server controls the shared object, there is no need in server-side scripts to call a server-side connect( ) method (there isn't one) or wait for an onSync( ) event.

Another difference from client-side ActionScript is that server-side shared objects don't have a data property. Assigning and retrieving values of server-side shared objects is done via the getProperty( ) and setProperty( ) methods. The following statement sets a property of the shared object. The first parameter in setProperty( ) is always a string property name, while the second parameter can be data of any built-in ActionScript datatypewith the same exceptions and caveats mentioned earlier:

userList_so.setProperty(userName, "public/chat/" + userName);

If an object, array, or other non-primitive data is placed in a shared object's slot and an element in the object or array is updated later, setProperty( ) must be called again to update the shared object. For example:

// Create an object.
var loc = {x: 10, y: 30};
// Copy it into the shared object property named location.
userList_so.setProperty("location", loc);
// Update the object.
loc.x = 89;
// You must copy the object back into the shared object to update it.
userList_so.setProperty("location", loc);

To delete a slot of a shared object in Server-Side ActionScript, call setProperty( ) and pass null as the second parameter. In Example 8-5, this deletes the entry for a user when her client disconnects:

userList_so.setProperty(client.userName, null);

To retrieve the value of a shared object's property in SSAS, use getProperty( ):

var userPath = userList_so.getProperty(userName);

If a property does not exist, getProperty( ) returns null.

Working with server-side shared objects is not as convenient as working with them in client-side Flash ActionScript. The data object of client-side shared objects is not available on the server, and you have to call setProperty( ) every time you want to update any part of an object or array within a slot. The data object and automatic updates available in Flash were left out of the server implementation for performance reasons. Detecting and managing changes to elements of the data object is both a CPU- and memory-intensive process. However, you should not shy away from scripting shared objects on the server. It is often the right place to control updates, as described in Chapter 15 and Chapter 18. Let's look at a quick example. In Example 8-5, clients are given read-only access to a shared object. The shared object is created before any client connections are accepted, and all the updates are done on the server side. It is not uncommon for applications to simply deny write access to every client, as follows:

application.onConnect = function (client) {
   client.writeAccess = ""; // No write access
   client.readAccess = "/"; // Read anything
};

Now consider what happens if the server doesn't create the shared object before the first client is allowed to connect. If a shared object does not exist when a client with read-only access attempts to connect to it, the connection attempt will fail. To avoid the problem, shared objects are often created and initialized in the application.onAppStart( ) method as in Example 8-5.

8.4.1. Server-Side onSync( )

In Flash, a shared object's onSync( ) handler can be defined either on SharedObject.prototype or on an individual shared object instance. On the server, you cannot use the prototype to define an onSync( ) handler for all shared objects; for performance reasons, onSync( ) handlers must be defined for an individual instance. One way to define an onSync( ) handler for a SharedObject instance is by using a function expression, also called a function literal:

ball_so = SharedObject.get("BallPosition");
ball_so.onSync = function (list) {
  trace("ball_so.onSync> list.length: " + list.length);
  for (var i in list) {
    trace("---------- list item number: " + i + " ------------");
    var info = list[i];
    for (var p in info) {
      trace(p + ": " + info[p]);
    }
  }
};

Another option is to use a function declaration (also called a function statement) to define the function and then assign it to an individual instance as necessary:

function genericOnSyncHandler (list) {
  trace("ball_so.onSync> list.length: " + list.length);
  for (var i in list) {
    trace("---------- list item number: " + i + " ------------");
    var info = list[i];
    for (var p in info) {
      trace(p + ": " + info[p]);
    }
  }
}
ball_so = SharedObject.get("BallPosition");
ball_so.onSync = genericOnSyncHandler;

The shared ball example does not require a main.asc script. However, if we wrote one and added the code from either of the previous examples to it, when you drag the ball around, the output in the App Inspector movie would be similar to:

Loading of app instance: chapter8/sharedBall/version_1 successful
ball_so.onSync> list.length: 2
---------- list item number: 0 ------------
code: change
name: ball_1_mc_x
oldValue: undefined
---------- list item number: 1 ------------
code: change
name: ball_1_mc_y
oldValue: undefined
ball_so.onSync> list.length: 1
---------- list item number: 0 ------------
code: change
name: ball_1_mc_y
oldValue: 82

Whenever a change occurs as a result of an update from a Flash movie, the list array passed into the server-side onSync( ) method includes information objects with a code value of "change" or "delete" and will always contain an oldValue propertyeven if its value is undefined. When a change is made using the server-side setProperty( ) method, onSync( ) is not called on the server; however, the client-side onSync( ) method is called for each copy of the shared object in connected Flash movies, subject to the constraints described in the next section.

8.4.2. Locking a Shared Object

Client-generated updates of shared objects do not run in the same thread on the server as SSAS, so shared object updates can occur in between server-side setProperty( ) method calls. To guarantee that a shared object cannot be updated by a client until a unit of work is complete on the server, use the shared object's lock( ) and unlock( ) methods. A locked shared object will not notify clients of updates until it is unlocked, so locking a shared object on the server is also a good way to group updates together so they are all sent in one batch to clients. The client-side shared object's onSync( ) method will be called only after all the data has been incorporated into the local copy of the shared object. The following server-side code demonstrates using lock( ) and unlock( ) properly:

// initPuzzlePieces(  ) creates a shared object for each puzzle piece
// containing the initial coordinates, x and y velocity, and state.
function initPuzzlePieces (nClips, xMin, xMax, yMin, yMax) {
   var so;
   puzzlePieces = [];
   for (var i = 0; i < nClips; i++) {
      so = SharedObject.get("PuzzlePiece_" + i, true);

      // Stop client update requests and stop sending updates to clients.
      so.lock( );

      // Select a random position within the travel limits.
      so.setProperty("x", Math.floor(Math.random( )*(xMax-xMin) + xMin));
      so.setProperty("y", Math.floor(Math.random( )*(yMax-yMin) + yMin));

      // Select a random direction in which to start moving this clip.
      so.setProperty("xVel", Math.floor(Math.random( )*12) - 5);
      so.setProperty("yVel", Math.floor(Math.random( )*12) - 5);

      // Mark the puzzle piece as being available.
      so.setProperty("state", "free");

      // Send the new properties for this shared object now.
      so.unlock( );

      // Keep a reference to each shared object.
      puzzlePieces.push(so);
   }
}

Locks can be nested, so be sure you have a matching unlock( ) call for every lock( ) you issue. FlashCom maintains a version number for the shared object as a whole and for each shared object slot. When lock( ) is called on a shared object, the version number is not incremented until the shared object is unlocked. The version number for the shared object is available in its version property and is normally incremented after every setProperty( ) call. The version number of each individual slot is not available to ActionScript.

8.4.3. Clearing a Shared Object

Three server-side approaches can be used to reliably clear all the properties of a shared object, so that none are left. One approach is to lock the shared object, get all its properties' names using getPropertyNames( ), and then delete each property using setProperty( ), as follows:

so = SharedObject.get("SOName");
so.lock(  );
var names = so.getPropertyNames(  );
for (i in names) {
  so.setProperty(names[i], null);
}
so.unlock( );

Executing a loop like this will also result in the client shared object's onSync( ) method being passed a list of information objects. Each item's code property will have a value of "delete." However, it is much simpler to use the server-side clear( ) method:

so = SharedObject.get("SOName");
so.clear(  );

In this case, each client's shared object onSync( ) handler will receive an array containing one information object with a code value of "clear."

The application.clearSharedObjects( ) method can also be used to clear shared objects as discussed in "Clearing and Deleting Persistent Shared Objects" later in this chapter.

8.4.4. Checking the Size of a Shared Object

The size( ) method, available only on the server, returns the number of properties in a shared object and is a convenient way to check whether a shared object is already in use:

if (so.size(  ) == 0) {
  // Initialize some shared object properties.
}