Thursday, July 23, 2009

Very Simple Remote Shared Object

Overview

In this example we'll see how you can use Flex's SharedObject class with Red5 to share data between connected clients. Data stored in an instance of the SharedObject class, when used remotely with a server, appears the same to all clients. When a client updates a property on a remote SharedObject instance, other clients see the same information. You can think of it like a replication object, a distributed cache, or a message bus. The data can be simple Strings or complex data structures. The server is capable of creating and updating SharedObjects as well. Updating from clients or servers both have their use in a variety of scenarios.

This example will show the most trivial case, where we want to sent simple Strings between connected clients.


Prerequisites

Client application

  1. In Eclipse, switch to the Flex Development Perspective.
  2. In the Flex Navigator view, right click and choose New -> Flex Project
  3. For a project name, use VerySimpleVideoPlayer, and then click Finish.
  4. In the project, open the file VerySimpleSharedObject.mxml
  5. Replace the default source with the following:


    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="connect()">
      
      <mx:Script>
        <![CDATA[
          /** A value of "success" means the client changed the shared object */
          private static const SUCCESS : String = "success";
          /** A value of "change" means another client changed the object or the server resynchronized the object. */
          private static const CHANGE : String = "change";
        
          
          private var _netConnection:NetConnection;
          private var _netStream:NetStream;
          private var _remoteSharedObject:SharedObject;
          
          /** We'll send randomly generated messages through the SharedObject to other clients. */
          [Bindableprivate var _nextMessage : String = int(Math.random()*100).toString();
        
          /** Called at creationComplete, this method will create the NetConnection to the server, 
           * required to establish the SharedObject.
           */
          public function connect() void {
            _netConnection = new NetConnection();
            _netConnection.objectEncoding = ObjectEncoding.AMF3
            _netConnection.client = this
        
            _netConnection.addEventListener(NetStatusEvent.NET_STATUS, onStatus);
            _netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            _netConnection.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            _netConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
        
              _netConnection.connect("rtmp://localhost/SimpleRed5Project");        
          }
          
          public function onStatusevent : NetStatusEventvoid {
            trace("NetConnection.onStatus " + event);
            if (event.info !== '' || event.info !== null) {  
              switch (event.info.code) {
                    case "NetConnection.Connect.Success":   createSharedObject();  break;
                    case "NetConnection.Connect.Closed":  trace("Disconnected");  break;
              }      
            }
          }
          
          private function createSharedObject() void {
            trace("Connected, creating SharedObject");
            _netStream = new NetStream(_netConnection);
            _netStream.addEventListener(NetStatusEvent.NET_STATUS, onStatus);
            _netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
            _netStream.client = this;    
            
            _remoteSharedObject = SharedObject.getRemote("myDataObj", _netConnection.uri, false);
            _remoteSharedObject.client = this;
            _remoteSharedObject.addEventListener(SyncEvent.SYNC, onSyncEventHandler);
            _remoteSharedObject.connect(_netConnection);          
          }
          
          public function securityErrorHandler(event : SecurityErrorEventvoid {  trace('Security Error: '+event);  }
          public function ioErrorHandler(event : IOErrorEventvoid {  trace('IO Error: '+event);  }
          public function asyncErrorHandler(event : AsyncErrorEventvoid {  trace('Async Error: '+event);  }  


          private function onSyncEventHandler(event : SyncEvent):void {
            trace("onSync code: " + event.changeList[0].code);  
            switch(event.changeList[0].code) {
              case SUCCESS:  processUpdate(event.target.data["message"], event.target.data["clientID"]);    break;
              case CHANGE:  processUpdate(event.target.data["message"], event.target.data["clientID"]);    break;
            }    
          }

           private function processUpdatepMessage : String, pClient : String void {
            _messageLabel.text = pMessage;
            _clientLabel.text = pClient;
          }
     
          private function sendMessage() void {
            _remoteSharedObject.setProperty("clientID", _clientID.text );
            _remoteSharedObject.setProperty("message", _nextMessageLabel.text;
            // Create a new random message for next time.
            _nextMessageLabel.text = int(Math.random()*100).toString();
          }
          
        ]]>
      </mx:Script>
      
      <mx:Label text="Message"  x="437" y="60"/>
      <mx:Label id="_messageLabel"  x="514" y="60"/>
      <mx:Label text="Updated by"  x="437" y="90"/>
      <mx:Label id="_clientLabel"  x="514" y="90"/>
      
      <mx:Label text="Client ID (randomly generated)" x="10" y="60"/>
      <mx:TextInput text="{int(Math.random()*100)}" id="_clientID"  x="199" y="58"/>
      
      <mx:Label text="Next message"  x="106" y="90"/>
      <mx:TextInput text="{_nextMessage}" id="_nextMessageLabel"  x="199" y="88"/>
      <mx:Button label="Send a message" click="sendMessage()"  x="199" y="118"/>
      
    </mx:Application>






Testing your application

  1. Launch your client application by right clicking on the project and choosing Debug As -> Flex Application
  2. Your browser should launch with the Flex application loaded.
  3. Copy the location from the browser bar.
  4. Launch a new browser window
  5. Paste the location you copied in step #3 into the address bar and load the URL. This should load the Flex application a second time.
  6. Arrange the browser windows so you can see both Flex application instances at the same time. It should preferably look like this:

    Note that each window should have a different Client ID. If you get the same numbers by random luck, you can change them manually or relaunch the application. In a non-example application, you wouldn't use random numbers on the client to generate IDs.
  7. With both windows visbible, click the "Send a message" button in one of your two browser windows. You should see the "Message" and "Updated by" fields update in both windows:


  8. Click the "Send a message" button in the other browser window. Again both "Message" and "Updated" fields should be updated, this time with the client ID of the second window. When a SharedObject is updated, a SyncEvent is processed by all clients, including the client that updated the SharedObject.


Further Reading

0 comments:

Post a Comment