Section 9.3. Client-to-Server Calls

9.3. Client-to-Server Calls

Clients can ask the server to perform some operation using the NetConnection.call( ) method, supported in Flash Player 6 and higher.

The syntax for NetConnection.call( ) is:

 my_nc.call(serverMethodName, resultObject  null, [, p1, ...p   n   ]); 

where my_nc is the instance name of a NetConnection object.

The first parameter of call( ) is serverMethodName , a string (such as "test" or "getServerTime") that specifies the name of the method to invoke on the server.

Almost any valid identifier name can be used as a method name; however, the name cannot include spaces and you shouldn't use reserved words and methods such as agent , call , getBandwidthLimit , getStats , ip , onAppStart , onAppStop , onConnect , onDisconnect , onStatus , protocol , ping , readAccess , referrer , setBandwidthLimit , or writeAccess .

The second parameter passed to call( ) can be either null or a result object (as explained shortly).

The optional parameters p1, ...p n are passed as parameters to the method on the server.

For the call to work, you must define a method on the server with the exact same name of your first parameter ( serverMethodName ) and attach it to the server-side Client object.

Let's see some examples.

9.3.1. Simple call( ) Example: "Server, Limit My Bandwidth"

As described in Chapter 5, FlashCom can limit how much data is sent to or received from each client using Client.setBandwidthLimit( ) . These limits allow users to have a great multiuser experience even at different connection speeds (modem, ADSL, T1, or LAN). The setBandwidthLimit( ) method is available only on the server-side Client object, which makes for a great example of a simple client-to-server call.

Place a ComboBox component on the main timeline, give it the instance name speed_cb , and attach the following client-side ActionScript to frame 1 of the Actions layer (a scripts-only layer you've created for that purpose):

 _root.speed_cb.addItem("Modem", {up: 33, down:33}); _root.speed_cb.addItem("DSL", {up: 100, down: 400}); _root.speed_cb.addItem("LAN", {up: 1000, down: 1000}); 

Now define a change handler for it, like this:

 _root.speed_cb.addEventListener("change", _root); _root.change = function (evt:Object):Void {   var selected = _root.speed_cb.value;   trace("Asking server to set my limits to: up:" + selected.up +         ", down:" + selected.down);   nc.call("setMyBandwidth", null, selected.up, selected.down); }; 

where nc is the NetConnection object used to connect this client to the FlashCom Server.

Here's how you can define the setMyBandwidth( ) method on the Client object in the server-side main.asc file:

 application.onConnect = function (clientObj) {   // ...some regular   onConnect(  )   code.   // Called by the client   clientObj.setMyBandwidth = function (clientToServer, serverToClient) {     this.setBandwidthLimit(1000*serverToClient/8, 1000*clientToServer/8);   };   application.acceptConnection(clientObj); }; 

When each client ( .swf movie) makes a connection to the server, FlashCom invokes main.asc 's application.onConnect( ) method, with a reference to the client connection ( clientObj ) as a first parameter. On this Client instance, the code defines the setMyBandwidth( ) method, which just calls setBandwidthLimit( ) on the Client instance itself (as represented by the keyword this ), using the parameters passed in to the call. Finally the code accepts the connection to the client movie (which receives a "NetConnection.Connect.Success" status in the NetConnection.onStatus( ) handler, if defined). When the user selects a value using the speed_cb combo box, change( ) is invoked, which uses the NetConnection.call( ) method to invoke setMyBandwidth( ) on the server, passing the two parameters provided. Since we defined setMyBandwidth( ) on clientObj , the method is found and invoked, and the Client.setBandwidthLimit( ) method is used to throttle this client's bandwidth. No return value or result is needed from the execution of setMyBandwidth( ) , so the second parameter of the NetConnection.call( ) method is set to null .

The Server-Side ActionScript (SSAS) language is strictly case-sensitive. Always copy and paste the method name from your call( ) parameter to the function definition (or vice versa) to avoid typos.


This sample showed how to execute a simple remote method from a client to the server and how to pass parameters to it. As we will see, the setMyBandwidth( ) method could also be defined on Client.prototype instead of on the instance ( clientObj ).

9.3.2. Other Parameter Types

In the previous example, we passed two integers to a server-side method. You can also pass arrays, like this:

 var myArray = [0,1,2]; nc.call("myMethod", null, myArray); 

or, more commonly, simple records (which in ActionScript are implemented as objects with properties containing data but not methods):

 nc.call("myMethod", null, {name:"Peldi", presenter:true, age:29}); 

As discussed later, you can also pass full-blown objects (with methods) as parameters to call( ) .

9.3.3. Result Object Example: "Server, What Time Is It?"

As mentioned earlier, remotely invoked methods can return a value to the invoking client. Let's see how to handle a return value. Imagine an application that displays the time on the server, so that users connected around the world can synchronize their watches (maybe they're planning a jewel heist).

Place a Button component on the main timeline and give it the instance name getServerTime_pb . Also create a dynamic text field on the main timeline and name it serverTime_txt . Now you can assign it a click handler by attaching this client-side ActionScript to frame 1 of the Actions layer:

 _root.getServerTime_pb.addEventListener("click", _root); _root.click = function (evt:Object):Void {   var resObj = new Object(  );   resObj.onResult = function (val):Void {     trace("The time on the server is:" + val);     _root.serverTime_txt.text = val;   };   nc.call("getServerTime", resObj); }; 

As you can see, we are passing a result object reference, resObj , as the second parameter of the call( ) invocation; it is used to obtain the result of the call. In order to be a valid result object, resObj must define an onResult( ) method. This method will be triggered by the Flash Player as soon as the result arrives back from the server (don't forget that, just like the call itself, this is an asynchronous operation; you should make no assumption as to how long it will take for the result to arrive ). Note that the onResult( ) method accepts a val parameter, which will contain the result returned from the remote method invocation.

Now let's define the getServerTime( ) method on the server:

 application.onConnect = function (clientObj) {   // ...some regular   onConnect( )   code.   // Called by the client   clientObj.getServerTime = function ( ) {     var now = new Date( );     return now.toUTCString( );   };   application.acceptConnection(clientObj); }; 

This example is very similar to the previous one. When each client ( .swf movie) makes a connection to the server application ( main.asc ), FlashCom invokes application.onConnect( ) and passes it a reference to the client connection ( clientObj ). On this object, we define the getServerTime( ) method, which returns the date as a string, using Date.toUTCString( ) . In this example, getServerTime( ) doesn't require any parameters, so none were passed to call( ) on the client side. When the user clicks the getServerTime_pb button, Flash invokes the click( ) handler. A new result object ( resObj ) is created, and the call is made to getServerTime( ) . The server executes the call and returns the string to the Flash Player, which invokes the onResult( ) handler for the registered result object. All that's left to do is trace( ) the value and set the text of the dynamic text field ( serverTime_txt ) to it.

The datatype of the val parameter received by resObj.onResult( ) is wholly dependent on the return value of clientObj.getServerTime( ) . In this example, it is a string, but it could be any native datatype.


9.3.4. Another Example: "Server, How Long Is This Stream?"

Here's a similar example that retrieves the length (in seconds) of a recorded .flv file from the server. As you have seen in Chapter 5, the Stream.length property is available only on the server. So if a Flash client wants to know how long a stream is in seconds, it has to ask the server. Here are some code snippets that do the trick. On the client movie, we'll do things a little differently this time, to show a different approach that might be useful sometimes. The outcome is the same: a call is made to the server and the result is displayed to the client through a text field. This code belongs on the client side:

 // Define a   StreamLengthResultHandler   object. StreamLengthResultHandler = function (owner, streamName:String):Void {   this.owner = owner;   this.streamName = streamName; }; StreamLengthResultHandler.prototype.onResult = function (val:Number):Void {   trace("The length in seconds of " + this.streamName + " is " + val);   this.owner.streamLength_txt.text = "Length of " + this.streamName +       ": " + val + "sec"; }; _root.getLength_pb.addEventListener("click", _root); _root.click = function (evt:Object):Void {   var name = _root.streamName_txt.text;   nc.call("getStreamLength", new StreamLengthResultHandler(_root, name), name); }; 

The difference in this client-side code is that we define a full-blown result handler object, with a constructor and an onResult( ) method. This technique is commonly used when doing asynchronous calls (when using Flash Remoting, for example). It's a way to have the result object "remember" some values that might come in handy when the result actually arrives. In this example, we store the owner and streamName properties passed into the constructor, and use them in the onResult( ) handler to display the stream name and to find the streamLength_txt text field in the correct scope ( _root , in this case).

The server-side code for this example is nothing new at this point:

 application.onConnect = function (clientObj) {   // ...some regular   onConnect( )   code.   // Called by the client   clientObj.getStreamLength = function (name) {     return Stream.length(name);   };   application.acceptConnection(clientObj); }; 

9.3.5. Client.method Versus Client.prototype.method

As we have seen in the previous examples, in order for a server-side method to be invoked by a .swf movie, it needs to be attached to a Client object. In the previous examples, we attached the method to the clientObj instance passed to application.onConnect( ) .

Alternatively, Server-Side ActionScript allows you to attach the method to Client.prototype , saving memory and processor time on the server. In ActionScript (and JavaScript and Server-Side ActionScript), if you attach a method to the prototype of the object (in this case, the Client class), every instance of that class (in our case, every client that connects) will contain that method automatically. Here's how you could rewrite the server-side methods used in the previous examples:

 Client.prototype.setMyBandwidth = function (clientToServer, serverToClient) {   this.setBandwidthLimit(1000*serverToClient/8, 1000*clientToServer/8); }; Client.prototype.getServerTime = function ( ) {   var now = new Date( );   return now.toUTCString( ); }; Client.prototype.getStreamLength = function (name) {   return Stream.length(name); }; 

You'll find that there are times when using one syntax is better than the other, and sometimes you'll want to use a combination of the two. Look at the following code, for example:

 application.onConnect = function (clientObj, username) {   // ...some regular   onConnect(  )   code.   if (username == "admin") {     // Define a method to be called by the admin user only.     clientObj.kickUser = function (id) {       application.disconnect(application.clientsByID[id]);     };   }   application.acceptConnection(clientObj); }; // This can be called by any client. Client.prototype.setMyBandwidth = function (clientToServer, serverToClient) {   this.setBandwidthLimit(1000*serverToClient/8, 1000*clientToServer/8); }; // This can be called by any client. Client.prototype.getServerTime = function ( ) {   var now = new Date( );   return now.toUTCString( ); }; 

As you can see in this example, we define setMyBandwidth( ) and getServerTime( ) on Client.prototype , making them available to be called by any client movie. Conversely, the onConnect( ) method defines another method, kickUser( ) , only for clients who connected with a username equal to "admin". If a non-admin client tries to make the following call:

 nc.call("kickuser", null, 3); 

it will fail, because the kickuser( ) method is not defined on that Client instance.

As a rule of thumb, attach methods needed by every Client object to Client.prototype . Define methods on client instances if you want a subset of your users to be able to invoke those methods.




Programming Flash Communication Server
Programming Flash Communication Server
ISBN: 0596005040
EAN: 2147483647
Year: 2003
Pages: 203

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net