Recipe 24.3.
Receiving Data
Problem
You want
to read data from a socket server.
Solution
For Socket instances, subscribe to the
socketData event and invoke one of the read
methods, such as readByte(
) or readInt( ),
in the event handler, making sure not to read past
bytesAvailable.
For XMLSocket instances, subscribe to
the data event and interpret the XML data received inside
of the event handler.
Discussion
Receiving data from a socket connection depends
on the type of socket you use. Both Socket and XMLSocket are
capable of receiving data from a server, but they do so using
slightly different techniques. Let's focus on how the Socket
class works first before discussing XMLSocket.
As you've learned in the introduction to this
chapter, sockets in Flash behave asynchronously. Therefore, it's
not possible to simply create a socket connection and attempt to
read data from the socket right away. The read methods don't
wait for data to be transferred from the server before returning.
Instead, you can only read data from a socket after the client has
already downloaded the data from the host server. It is an error to
try and read data from a Socket before any data is
available.
To know when data is available to be read, the
socketData event is broadcasted from Socket
instances. By adding an event listener for the socketData
event, your event handler is invoked anytime there is new data
received from the socket server. Inside the event handler is where
you write code to read and interpret the received data.
To read the data sent from the server, the
Socket class provides a number of different read
methods, depending on the type of data you want to read. For
instance, you can read a byte with the readByte( ) method,
or read an unsigned integer with the readUnsignedInt( )
method. See Table 24-1 for a list
of the different datatypes that can be read from the socket server,
what the return value is, and how many bytes the read method
consumes.
Table 24-1. Socket read methods for
various datatypes
Method : Return type |
Description |
Bytes read |
readBoolean( ):Boolean
|
Reads a Boolean value from the socket |
1 |
readByte( ):int
|
Reads a signed byte from the socket |
1 |
readDouble( ):Number
|
Reads an IEEE 754 double-precision
floating-point number from the socket |
8 |
readFloat( ):Number
|
Reads an IEEE 754 single-precision
floating-point number from the socket |
4 |
readInt( ):int
|
Reads a signed 32-bit integer from the
socket |
4 |
readObject( ):*
|
Reads an AMF-encoded object from the
socket |
n
|
readShort( ):int
|
Reads a signed 16-bit integer from the
socket |
2 |
readUnsignedByte( ):uint
|
Reads an unsigned byte from the
socket |
1 |
readUnsignedInt( ):uint
|
Reads an unsigned 32-bit integer from the
socket |
4 |
readUnsignedShort( ):uint
|
Reads an unsigned 16-bit integer from the
socket |
2 |
readUTF( ):String
|
Reads a UTF-8 string from the socket |
n
|
There are also two additional read methods not
covered in Table 24-1. They are
readBytes( ) and readUTFBytes( ). The readBytes(
) method is the only Socket read method to not return a
value, and it takes the following three parameters:
bytes
-
A flash.util.ByteArray instance to read
the data from the socket into.
offset
-
A uint value specifying the offset into
bytes where read data from the socket should start being placed.
The default value is 0.
length
-
A uint value for the number of bytes to
read. The default is 0, meaning all available data will be read
from the socket into the bytes ByteArray.
The readUTFBytes( ) method, on the other
handle, takes a single length parameter specifying
the number of UTF-8 bytes to read, and it returns the String
corresponding to the read bytes.
|
Before reading data from a Socket, it is
important to check the socket's bytesAvailable property
first. Attempting to read more data than what is available will
result in a flash.errors.EOFError.
|
|
The following code example connects to a socket
server, and reads and displays the data sent from the server one
byte at a time:
package {
import flash.display.Sprite;
import flash.events.ProgressEvent;
import flash.net.Socket;
public class SocketExample extends Sprite {
private var socket:Socket;
public function SocketExample( ) {
socket = new Socket( );
// Listen for when data is received from the socket server
socket.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData );
// Connect to the server
socket.connect( "localhost", 2900 );
}
private function onSocketData( event:ProgressEvent ):void {
trace( "Socket received " + socket.bytesAvailable + " byte(s) of data:" );
// Loop over all of the received data, and only read a byte if there
// is one available
while ( socket.bytesAvailable ) {
// Read a byte from the socket and display it
var data:int = socket.readByte( );
trace( data );
}
}
}
}
In the preceding example, if the socket server
sends back a message (such as "Hello"), the output of the code
would look like this when a client connects:
Socket received 5 byte(s) of data:
72
101
108
108
111
|
Once data is read from the Socket, it
cannot be read again. For example, after reading a byte, that byte
cannot be "put back" and read as part of an int later.
|
|
When the data received by a Socket object
is ASCII text, you can
reconstruct a string by using the readUTFBytes( ) method. The readUTFBytes( ) method requires that you tell
it how many bytes to read and convert to a string. You can use
bytesAvailable to read all the bytes:
var string:String = socket.readUTFBytes(socket.bytesAvailable);
The XMLSocket class behaves in a similar
manner to the Socket class in regard to how it receives data
from the server. In both cases, an event listener must be used to
be notified that data is available due to Flash's asynchronous
socket implementation. However, the process used to actually read
the data is very different.
An XMLSocket instance dispatches a
data event when data has finished downloading from the
server. The data event, defined by the flash.events.DataEvent.DATA
constant, contains a String data property that
contains the information received from the server.
|
When using XMLSocket, the data returned
from the server is always interpreted as a String. There are
no specific read methods for various datatypes.
|
|
The data returned from the server is
the raw server response. Because of this, you're not limited to
just using XML with XMLSocket connections, but rather
you can send and receive plain String information as well.
If you're expecting XML back from the server, however, you
must first convert the data into an XML instance before
working with it.
The following code example initiates a
connection over XMLSocket to a server running on port 2900
on the local computer. After the connection is successfully made, a
<test> message is sent to the server. The
onData event listener handles the response from the
server, which in this case is the string
<response><test
success='true'/></response>. You can see that the
data property of the event passed to
onData is just a String, and that the
XML constructor is
used to convert the String into an XML instance.
Finally, E4X syntax is used to output a portion of the
converted XML (for more information about working with
XML and using E4X, see Chapter
21):
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.DataEvent;
import flash.net.XMLSocket;
public class SocketExample extends Sprite {
private var xmlSocket:XMLSocket;
public function SocketExample( ) {
xmlSocket = new XMLSocket( );
// Connect listener to send a message to the server
// after we make a successful connection
xmlSocket.addEventListener( Event.CONNECT, onConnect );
// Listen for when data is received from the socket server
xmlSocket.addEventListener( DataEvent.DATA, onData );
// Connect to the server
xmlSocket.connect( "localhost", 2900 );
}
private function onConnect( event:Event ):void {
xmlSocket.send( "<test/>" );
}
private function onData( event:DataEvent ):void {
// The raw string returned from the server.
// It might look something like this:
// <response><test success='true'/></response>
trace( event.data );
// Convert the string into XML
var response:XML = new XML( event.data );
// Using E4X, access the success attribute of the "test"
// element node in the response.
// Output: true
trace( response.test.@success );
}
}
}
|
Before the data event can be
dispatched, the XMLSocket instance must detect the null byte
('\\0') from the server. That is, sending a string from
the server is not enough for the client to receive it. Instead, the
string must be terminated by the null byte.
|
|
See Also
Recipes 18.2,
24.1,
and 24.4
|