Monday, January 4, 2010

Section 4.11. Networking







4.11. Networking

4.11.1. Communicate on a Socket

4.11.1.1. Problem

You would like to communicate with a server using a protocol
that is not directly supported by Adobe AIR (e.g., communicate
with an FTP server).

4.11.1.2. Solution

Use the Socket class in
the AIR API to send binary or text data to the server and
register for events that will alert you to incoming data from the
server.

4.11.1.3. Discussion

When communicating using protocols other than those directly
supported by Adobe AIR, you may need to use the Socket API. The Socket
API is an asynchronous API that lets you send data to a persistent
socket endpoint and receive data from it in real time. You do not need
to create a new Socket instance for
each set of data sent to the same endpoint. The connection can be kept
alive for the entire conversation between your client and the service
to which you're connecting. This is the typical flow when using the
Socket API:

  1. Create a connection to the endpoint.

  2. Listen for notification of connection success or
    failure.

  3. Queue data that will be sent to the endpoint.

  4. Send the data to the endpoint.

  5. Listen for data incoming from the endpoint.

  6. Repeat steps 3 through 5.

  7. Close the connection.

The first step is to create a connection to the socket endpoint
that consists of a host and a port number. For example, to connect to
an endpoint the host might be foo.com and the port number might be
5555. Create the instance of the Socket class and connect to the endpoint
using that information. At this time, we will also set up our
listeners to listen for the different events that the Socket can
dispatch:

var socket = new air.Socket();
socket.addEventListener( air.Event.CONNECT, onSocketOpen );
socket.addEventListener( air.ProgressEvent.SOCKET_DATA,
onSocketData );
socket.connect( 'foo.com', 5555 );


We will also need to create the functions to handle the events
for which we subscribed. The first event is the air.Event.CONNECT event. This event will tell us when the socket has been
initiated and when communication with the service behind the endpoint
is possible. In this example, we are sending the bytes of a UTF-8
encoded string to the service:

function onSocketOpen( event )
{
// This queues up the binary representation of the
// string 'Bob' in UTF-8 format to be sent to the
// endpoint.
socket.writeUTFBytes( "Bob" );

// Send the actual bytes to the server and clear
// the stream. We then wait for data to be sent
// back to us.
socket.flush();
}


The air.ProgressEvent.SOCKET_DATA event
is dispatched whenever data is received. The service we
are connecting to uses a simple protocol: we send a UTF-8
encoded string and it returns a UTF-8 encoded string. This
makes accessing the data sent back to us very simple. To access this
data, we measure the total number of bytes of data available on the
Socket and read that many bytes as a UTF-8 encoded string using the
readUTFBytes() method of the
Socket class.

function onSocketData( event )
{
var data =
socket.readUTFBytes( socket.bytesAvailable );
air.trace( data ); // Hello Bob
}


In our example, the protocol of communication was just a single
string. In some cases, depending on the service with which you're
communicating, you may need to send and receive other data types. The
Socket class provides methods for
reading and writing many data types, such as ints,
Booleans, floats, and more. For example, if we were
talking with a fictional service that required us to send a Boolean followed by an int, our onSocketOpen function in the preceding
example could look like this:

function onSocketOpen( event )
{
// First send the boolean
socket.writeBoolean( true );
// Now send an int
socket.writeInt( 10 );

// Now we send the bytes to the service and
// clear the buffer.
socket.flush();
}


This example provides a baseline of functionality that can be
expanded upon to speak to many different protocols. The only current
limitation is that there is not currently an SSL Socket implementation
in AIR. For secure communication you will be limited to HTTPS:

<html>
<head>

<title>Communicating on a Socket</title>
<script type="text/javascript" src="AIRAliases.js">
</script>

<script>
var socket = null;

function init()
{
socket = new air.Socket();

// Create our listeners which tell us when the Socket
// is open and when we receive data from our service.
socket.addEventListener( air.Event.CONNECT,
onSocketOpen );
socket.addEventListener( air.ProgressEvent.SOCKET_DATA,
onSocketData );

// Connect to our service, which is located at
// host foo.com using port 5555.
socket.connect( 'foo.com', 5555 );
}

function onSocketOpen( event )
{
// This queues up the binary representation of the
// string 'Bob' in UTF-8 format to be sent to the
// endpoint.
socket.writeUTFBytes( "Bob" );

// Send the actual bytes to the server and clear
// the stream. We then wait for data to be sent
// back to us.
socket.flush();
}

function onSocketData( event )
{
var data = socket.readUTFBytes( socket.bytesAvailable );
air.trace( data ); // Hello Bob
}
</script>

</head>
<body onload="init()">
</body>
</html>




4.11.2. Upload a File in the Background

4.11.2.1. Problem

The application user has created numerous files offline, and you now want to send those
to the server without blocking the user from doing any additional
work.

4.11.2.2. Solution

The File class in Adobe AIR
provides an upload() method
that is designed specifically for this purpose, without
having to create and manage HTML forms.

4.11.2.3. Discussion

The File.upload() method can
upload files via HTTP/S to a server for additional processing. The
upload takes places just like a traditional multipart file upload from
an HTML form, but without the need to manipulate forms on the client.
The upload process also takes place asynchronously in the background,
allowing the application to continue processing without
interruption.

NOTE

The implementation of the receiving server is beyond the scope
of this example. Numerous technologies, and tutorials for these
technologies, elegantly handle file upload. You're encouraged to
investigate your options.

The primary events that are useful are ProgressEvent.PROGRESS and Event.COMPLETE.
These events handle notifying the application of upload progress, and
when an individual upload is complete, respectively:

var file =
new air.File.documentsDirectory.
resolvePath( 'myImage.jpg' );

file.addEventListener( air.ProgressEvent.PROGRESS,
doProgress );
file.addEventListener( air.Event.COMPLETE,
doComplete );


ProgressEvent contains
various properties that can help in reflecting upload progress in the
user interface. The most notable of these properties are ProgressEvent.bytesLoaded and ProgressEvent.bytesTotal, which show how
much of the file has been uploaded and the total size of the file.
Event.COMPLETE is broadcast once
the upload is complete.

To start the upload, you first need a valid File object that points to a resource on
disk.

Once a valid file reference is established, developers will want
to call the File.upload() method.
The File.upload() method can take
three arguments, the first of which is a URLRequest object that contains information
about where the file should be sent. The URLRequest object can also contain
additional data to be passed to the receiving server. This additional
data manifests itself as HTML form fields might during a traditional
multipart file upload:

var request = new air.URLRequest( 
'http://www.mydomain.com/upload' );
file.upload( request, 'image', false );


The second argument provided to the File.upload() method call is the name of the form field that contains the
file data.

The third argument is a Boolean value that tells the upload process
whether it should try a test before sending the actual file. The test
upload will POST approximately 10
KB of data to the endpoint to see if the endpoint responds. If the
service monitoring capabilities of Adobe AIR are not being used, this
is a good way to check for potential failure of the process.

NOTE

More than one great web application has been caught by this
subtlety. If the server is expecting the file data outright, a test
upload will almost assuredly cause an error. If you intend to use
the test facility, be sure that your server code is prepared to
handle the scenario.

function doProgress( event )
{
var pct = Math.ceil( ( event.bytesLoaded / event.
bytesTotal ) * 100 );
document.getElementById( 'progress' ).innerText =
pct + "%";
}


The Event.COMPLETE event is
relatively straightforward in that it signals the completion of the
upload process. This is a good place to perform any filesystem
maintenance that the application might otherwise need to accomplish.
An example would be removing the just-uploaded file from the local
disk to free up space. Another task that might be accomplished in the
Event.COMPLETE handler is to start
the upload of subsequent files:

<html>
<head>

<title>Uploading a File in the Background</title>

<style type="text/css">
body {
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 11px;
color: #0B333C;
}
</style>

<script type="text/javascript" src="AIRAliases.js"></script>

<script type="text/javascript">
var UPLOAD_URL = 'http://www.ketnerlake.com/work/watcher/
upload.cfm';

var file = null;

function doComplete( e )
{
document.getElementById( 'progress' ).style.visibility =
'hidden';
document.getElementById( 'progress' ).innerText =
'Uploading... 0%';

document.getElementById( 'upload' ).disabled = null;
}

function doLoad()
{
file = air.File.desktopDirectory;
file.addEventListener( air.Event.SELECT, doSelect );
file.addEventListener( air.ProgressEvent.
PROGRESS, doProgress );
file.addEventListener( air.Event.
COMPLETE, doComplete );

document.getElementById( 'upload' ).
addEventListener( 'click', doUpload );
}

function doProgress( e )
{
var loaded = e.bytesLoaded;
var total = e.bytesTotal;
var pct = Math.ceil( ( loaded / total ) * 100 );

document.getElementById( 'progress' ).innerText =
'Uploading... ' +
pct.toString() + '%';
}

function doSelect( e )
{
var request = new air.URLRequest( UPLOAD_URL );

request.contentType = 'multipart/form-data';
request.method = air.URLRequestMethod.POST;

document.getElementById( 'upload' ).disabled = 'disabled';
document.getElementById( 'progress' ).style.visibility =
'visible';

file.upload( request, 'image', false );
}

function doUpload()
{
file.browseForOpen( 'Select File' );
}
</script>

</head>
<body onLoad="doLoad();">

<input id="upload" type="button" value="Upload" />
<div id="progress" style="visibility: hidden">Uploading...
0%</div>

</body>
</html>











No comments: