Ryerson Flash Communication Development Server  
  Ryerson Home > Ryerson Flash Communication Development Server > Lobby and Chat Rooms Test Application

Lobby and Chat Rooms Test Application


Introduction

This test application extends some of the basic ideas presented in Lobby and Rooms Test Applications to a working test application that includes:

This is only a test application. Please see Macromedia's Flash Communication Components and Mike Chambers and Greg Burch's chat for more fully developed chat systems.

The basic test scenario

To keep things simple this test application doesn't do too much:

The application makes use of two shared objects in the lobby instance of the application. The room instances connect to the lobby and then get these two shared objects. One object, the room list, is used to provide public information about who is in each room. The user list shared object contains confidential information such as ticket values and the time window the ticket is valid in.

Using tickets to maintain client identity

In this small test application anonymous users select a user name before connecting to the lobby. If the user name is not being used by anyone else it becomes their unique name while visiting the lobby and the rooms until they leave. If it is being used by someone else the user has to try to connect with another name. To avoid complications such as there being two users with the same name, one in the lobby and another in a room, user names must travel with the user's client. In addition the system should force users to connect to the lobby first where they aquire their user name. Doing this might look simple at first. All that has to happen is that the client needs to remember the user name and use it when it connects to a room or back to the lobby. Unfortunately, this simple approach will not stop anyone who wants to hack a Flash movie from grabbing someone else's user name and masquerading as that person. One way to insure that the client that disconnects from the lobby is the same client that is attempting to connect to a room is to issue a unique ticket to the client. The ticket acts something like a transfer ticket in a public tansit system that people use to change buses. The client must present the ticket when it is changing connections. The ticket is only good for a limited time and can only be used once. Here is the sequence of events that would be used to allow a client to disconnect from the lobby and then connect to a room:

  1. The client requests a ticket from the lobby using a remote method call.
  2. The lobby application generates a ticket using a timestamp and random number.
  3. The lobby application stores the ticket in the user list shared object so the room instances can check it there.
  4. The client receives the ticket from the lobby and disconnects.
  5. The client connects to the room and passes it the user name and ticket.
  6. The room application looks up the ticket and user name in the lobby's user list shared object.
  7. If the ticket is identical and has not expired the client's connection is accepted. Otherwise it is rejected.

A hacked client that attempts to masquerade as someone else will not have the ticket and their connection attempt will be rejected.

The first attempt

In the first attempt to make this work a User class was defined. Each server-side client object is assigned a user object that contains information about the user including a ticket if they have one. This is setup whenever a client connects to the lobby. The simplified code snippet below shows this:

client.writeAccess = "/../../../"; // Don't want this client writing anything!
client.readAccess = "/public";     // Can read the roomList_so but not the user list.
client.user = new User(userName, client.ip, 0, client.referrer);

userList_so.setProperty(userName, client.user);
application.acceptConnection(client);
client.call('lobbyWelcome', null, client.user);

All the User objects are also stored in the lobby's userList_so shared object. The room instances connect to the lobby instance and proxy the userList_so shared object. When a client (the Flash movie) is ready to go to a room it asks for a ticket using a remote method call:

   lobby_nc.call('user.getTicket', this.ticketHandler);

On the server, the getTicket method of the User object is called and the return value is passed back to the Flash movie's ticketHander object's onResult method. The ticket handler class is relatively simple:

function TicketHandlerClass(requestor, current_nc, destination_nc){
   this.requestor = requestor;
   this.current_nc = current_nc;
   this.destination_nc = destination_nc;
}


TicketHandlerClass.prototype.onResult = function(result){
   this.requestor.acceptTicket(result, this.current_nc, this.destination_nc);
}

In this case the acceptTicket method of another object in the Flash movie is called. It disconnects from the lobby and tries to connect to the room. It passes the userName and ticket to the room instance it is trying to connect to. Here is the source:

ApplicationManager.prototype.acceptTicket = function(ticket, current_nc, destination_nc){
   this.ticketHandler = null;
   if (ticket){
      this.user.ticket = ticket;
      current_nc.close();
      destination_nc.setAddress('rtmp:/' + this.roomName);
      destination_nc.connect(this.user.userName, this.user.ticket);
   }
   else{
      this.user.ticket = 0;
   }
}

If you try the application using the link below you will see that most of the time this simple "transfer ticket" system works. But sometimes it fails. It does this because two things are happening simultaneously on two different computers. On the server, the user's ticket is stored in the user list shared object. But it takes time for the change in the shared object to be replicated to all the other instances. This is illustrated in the simplified interaction diagram below that shows the userList_so.setProperty statement being used to set the ticket in the lobby:

After the ticket is set in the lobby, the copy of the userList_so object in the room instance is updated by the server at the same time that the client is disconnecting from the lobby. So, it takes a little time before the room instance's copy of the shared object is updated. At the same time this is happening the client is disconnecting from one instance and trying to connect to another one. If the ticket has not been updated yet in the copy of the user list where the client is trying to connect to, the client's ticket will not appear to be valid and the connection will be rejected. Here is an illustration of how this can happen:

There are two competing processes at work. The time it takes for the client to disconnect and attempt to reconnect to the room and the time it takes for the userList_so to be updated in the room instance after being set in the lobby instance. In the illustration above it took longer to update the user list so the connection attempt by the client will fail. Some solutions to this problem are described below. Here is the first attempt that demonstrates this problem:

Use the link below to create a number of pop up windows to try this out. Try entering a name into the login screen and explore the lobby and simple chat rooms. At some point when you are moving from lobby to room or back you may find you simply lose your connection to the system. Further down on this page you will find the solution to this problem.

Open the swf file in a browser window.

Here are the source files for the client - many of these are quite short:

Here are the server-side script files:

 

The corrected application

There are a number of ways to fix the problem described earlier. All of them have one thing in common. The client must wait until the ticket becomes available to the destination intance of the application before trying to change connections. The following briefly outlines some ways to do this:

Any of these will work but the second one is illustrated here because it is easy to code and doesn't require remoting. A production system would likely use a database instead of a shared object for this function. Here is a simplified interaction diagram that shows that this approach will always work as the client always waits until the shared object is updated on the destination instance.

In this example the client does nothing after the getTicket remote method call to the lobby. Instead a sequence of remote method calls lead the client being told to change connections after the user list shared object has been updated in the destination instance.

Open the swf file in a browser window.

Here are the source files for the client - many of these are quite short:

Here are the server-side script files:

 


This document was written by Brian Lesser and last updated: