ProblemYou 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. SolutionCreate 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. DiscussionA 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:
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 AlsoRecipes 18.2, 24.1, and 24.3 |