Recipe 24.4. Handshaking with a Socket Server


Problem

You want to do handshaking with a socket server and need to know what the received data's context is to know how to process it.

Solution

Create different constant variables to represent states of the protocol. Use the constants to map particular processing functions with the corresponding state. In a socketData event handler, call the appropriate function by invoking it through the state map.

Discussion

A common scenario when connecting to a socket is going through a handshake process. Typically, the server initially sends data to the client. The client then responds to the data in a particular manner, and the server responds again accordingly. This entire process repeats until the handshaking is complete and a "normal" connection is established.

It gets difficult to process the response from the server because the socketData event handler does not keep track of context. That is, there is no "why" sent along with the server response, or no "this data is in response to" processing directive. Knowing how to process the response from the server is not usually something that can be gathered through the response itself, especially when the response varies. Perhaps one response returns two bytes and another returns an integer followed by a double. You can begin to see how this presents itself as a problem.

The solution is to create various state constants to represent the different contexts in which the server sends data to the client. By associating each of these constants with a particular function to handle the data, you can easily call the correct processing function based on the current state of the protocol.

Consider the following handshaking scenario that happens when you connect to a socket server:

  1. The server responds immediately when it connects with an integer representing the highest version of the protocol that the server supports.

  2. The client responds with an integer to indicate the actual version of the protocol that should be used for communication.

  3. The server sends back an 8-byte authentication challenge.

  4. The client sends the authentication challenge back to the server.

  5. The server closes the connection if the client response was not what the server was expecting, or, at this point, the protocol moves into a normal operating mode and the handshaking is complete.

In reality, Step 4 involves a more secure response to an authentication challenge. Instead of just sending back the challenge verbatim, you would really want to use some sort of encryption with a user-supplied key. Perhaps the client asks the user for a password, and then the password entered is used as the encryption key for the 8-byte challenge. The encrypted challenge is then sent back to the server. If this challenge response matches what the server expected, then the client knew the right password and the connection should be allowed.

To implement the handshaking processed outlined, you first want to create constants to represent the different kinds of data returned from the server. First, there is determining the version from Step 1. Second, there is receiving the challenge from Step 3. Finally, there is the normal operating mode from Step 5. These can be represented by the following constants:

public const DETERMINE_VERSION:int = 0; public const RECEIVE_CHALLENGE:int = 1; public const NORMAL:int = 2;

It doesn't matter what values are given to the constants. Rather, the only important part is that all the values are different so no two constants represent the same int value.

The next step is to create different functions to process the data. The three functions created will be named readVersion( ), readChallenge( ), and readNormalProtocol( ). After the functions have been defined, a map must be created to associate one of the previous state constants with the function used to process the data received during that state. The code for that looks like this:

stateMap = new Object(  ); stateMap[ DETERMINE_VERSION ] = readVersion; stateMap[ RECEIVE_CHALLENGE ] = readChallenge; stateMap[ NORMAL            ] = readNormalProtocol;

The final step is to code the socketData event handler in such a way that the correct processing function is invoked based on the current state of the protocol. To do this, a currentState int variable is created. Then, using the stateMap, the processing function is invoked by looking up the function associated with currentState:

var processFunc:Function = stateMap[ currentState ]; processFunc(  ); // Invoke the appropriate processing function

There is a little bit of bookkeeping involved in this process. Be sure to update currentState in your code to accurately reflect the current state of the protocol.

The entire code example to process the handshaking scenario previously described looks like the following:

package {   import flash.display.Sprite;   import flash.events.ProgressEvent;   import flash.net.Socket;   import flash.utils.ByteArray;   public class SocketExample extends Sprite {        // The state constants to describe the protocol     public const DETERMINE_VERSION:int = 0;     public const RECEIVE_CHALLENGE:int = 1;     public const NORMAL:int = 2;          // Maps a state to a processing function     private var stateMap:Object;          // Keeps track of the current protocol state     private var currentState:int;          private var socket:Socket;        public function SocketExample(  ) {       // Initialzes the states map       stateMap = new Object(  );       stateMap[ DETERMINE_VERSION ] = readVersion;       stateMap[ RECEIVE_CHALLENGE ] = readChallenge;       stateMap[ NORMAL            ] = readNormalProtocol;              // Initialze the current state       currentState = DETERMINE_VERSION;       // Create and connect the socket       socket = new Socket(  );       socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );       socket.connect( "localhost", 2900 );     }          private function onSocketData( event:ProgressEvent ):void {       // Look up the processing function based on the current state       var processFunc:Function = stateMap[ currentState ];       processFunc(  );     }          private function readVersion(  ):void {       // Step 1 - read the version from the server       var version:int = socket.readInt(  );              // Once the version is read, the next state is receiving       // the challenge from the server       currentState = RECEIVE_CHALLENGE;              // Step 2 - write the version back to the server       socket.writeInt( version );       socket.flush(  );     }          private function readChallenge(  ):void {       // Step 3 - read the 8 byte challenge into a byte array       var bytes:ByteArray = new ByteArray(  );       socket.readBytes( bytes, 0, 8 );              // After the challenge is received, the next state is       // the normal protocol operation       currentState = NORMAL;              // Step 4 - write the bytes back to the server       socket.writeBytes( bytes );       socket.flush(  );     }          private function readNormalProtocol(  ):void {       // Step 5 - process the normal socket messages here now that       // that handshaking process is complete     }   } }

See Also

Recipes 18.2, 24.1, and 24.3




ActionScript 3. 0 Cookbook
ActionScript 3.0 Cookbook: Solutions for Flash Platform and Flex Application Developers
ISBN: 0596526954
EAN: 2147483647
Year: 2007
Pages: 351

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