Ryan has posted 9 posts at DZone. View Full User Profile

Communicating with Flex and PHP over Sockets

02.23.2010
| 37778 views |
  • submit to reddit

There are a number of ways to communicate between Flex and PHP but one of the more interesting is over sockets. Socket communication lets developers create near real-time communication by pushing information directly to the client. In this article you'll see the basics of how to use PHP as the socket server and connect it to a Flex application.

Sockets are a practical and efficient way to send data from a server to a client. In a traditional HTTP model, the client sends a request to the server, the server processes it, the client receives a response, and finally disconnects from the server. To do this, HTTP requires the client to send a significant amount of extra information, including how long to maintain the connection, security and permission information, and information that identifies the request. When sending many bursts of data in a short period of time, as you might do with stock quotes or a chat application, your application is going to have to reconnect multiple times and perform several back and forth exchanges with the server, sending all that extra information each time to get the data. Each transaction can take twice as long since the client is sending a request and waiting for the response to reach it. That can be problematic for applications that need instant or near real-time feedback. Fortunately, there is an easy solution to this problem: sockets.

Unlike with HTTP, when you use sockets, the application makes a single request to the server; the server opens a connection and maintains it so that it can push data out to the client whenever it wants. Both the client and server bind to a socket so they can listen for any changes in information, and both client and server can send information over that socket. That means the client will receive any data sent by the server over a socket without having to make a request. As a result you can cut the time it takes to receive data in half and your application can send and receive that data in near real-time.

In order to use sockets with Flash Player you have to perform a couple of extra steps. For security reasons, Flash Player requires a policy file to be on the server that the client will be connecting to. There is an article on the Adobe Developer Center that covers some of the rationale behind this requirement and the steps needed to implement it in detail. Before Flash Player sends a socket request out, it first makes another socket connection on a specific port (843) and then expects the response to be a policy file that contains information about who is allowed to connect and on which ports Flash Player can make connections. The Developer Connection article above explains what the policy files should contain. The policy file used in this article is below; it is saved in the same directory as your PHP code as flashpolicy.xml.


<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">

<!-- Policy file for xmlsocket://socks.example.com -->
<cross-domain-policy>

<!-- This is a master socket policy file -->
<!-- No other socket policies on the host will be permitted -->
<site-control permitted-cross-domain-policies="master-only"/>

<!-- Instead of setting to-ports="*", administrator's can use ranges and commas -->
<!-- This will allow access to ports 123, 456, 457 and 458 -->
<allow-access-from domain="*" to-ports="*" />

</cross-domain-policy>

 

On the server, that means you have to program for two cases; the first to handle Flash Player connecting to get the policy file and the second to handle Flash Player connecting to share data. For the purpose of this tutorial I've divided those two cases into separate files.

I’ll cover the PHP code to provide the policy file first.

 

Handling policy file requests

First you need to get the contents of the flashpolicy.xml file and then set up the socket. The socket_create function takes three parameters that let you define exactly what protocol to use when creating the socket.

<?php
// Get the flashpolicy.xml file so we can send it to the Flash Player
$filename = "./flashpolicy.xml";
$content = file_get_contents($filename);

// Create a socket that uses the IPv4 protocol over TCP. By changin the
// parameters passed into the function we could create an IPv6 socket and/or
// create a socet that would use UDP.
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

if (!is_resource($socket)) {
echo 'Unable to create socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo "Socket created.\n";
}
?>

 

Once you've created it, you need to bind to a host and a specific port. In this case because, Flash Player is going to request the data from port 843, that is the port to use. After the binding step, the code listens on the bound socket for any data that might be sent from the client.

//  Next we bind to a specfic host and port. In this case, the port is 843 because 
// we're listening for Flash Player's policy file request.
if (!socket_bind($socket, 'localhost', 843)) {
echo 'Unable to bind socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo "Socket bound to port 843.\n";
}


// Once we successfully bind to the host and port, we can start listening for requests
// from the client.
if (!socket_listen($socket,SOMAXCONN)) {
echo 'Unable to listen on socket: ' . socket_strerror(socket_last_error());
} else {
echo "Listening on the socket.\n";
}

 

With that the socket should be set up correctly and you can start sending and receiving data. Socket communication is very different from the request-response model that you may be familiar with, and as a result the code may seem counterintuitive. Unlike a typical PHP page that loads in a browser when the browser requests it, socket communication is continuous. As a result you never want to be finished "loading" the page. To achieve that end, the rest of the code is placed in a while loop that continues to run, accepting any connections that come in. 

Within that while loop, you need to create a connection by accepting whatever tries to connect to the socket. If the connection is successful, you have a connection object that you can then read from or write to.

// Unlike a typical PHP page which runs and then finishes, we want to always be 
// looking for new connections. So we use an infinite loop that will accept connections
// and then handle them.
while(true)
{
$connection = @socket_accept($socket);
if ($connection)
{
echo "Client $connection connected!\n";
} else {
echo "Bad connection.";
}

 

If Flash Player connects successfully, then the first thing it writes to the socket is a request to the server for the policy file. To handle that on the server, a call to the socket_read method is needed to get the data that's being sent from the client so it can be processed. If the server-side code receives the string "<policy-file-request/>" followed by a null character then it sends the data from the policy file, otherwise it simply closes the connection.


     // Read the data the client is sending and set it as the input variable. 
// If that variable is a policy file request then we serve up the policy
// file.
$input = socket_read($connection,1024);
echo $input."\n";

if( $input == "<policy-file-request/>\0")
{
echo "Policy file request\n";
} else {
echo "Unknown request\n";
socket_close($connection);
break;
}

 

Finally, you need to send the requested policy file to Flash Player and then exit the while loop. In this example, the policy file defines a wide open policy that lets anyone connect, but normally you want to limit the hostnames and ports to control how Flash Player can connect to your socket server.


     // Send the data from the policy file to the client by writing it to the 
// socket.
socket_write($connection,$content,strlen($content));
socket_write($connection,"\0",strlen("\0"));
socket_close($connection);
}

 

You now have the server code that will let Flash Player connect to a socket and request the policy file. Save this file as socket_authentication.php. Running this code is different from running it on a website. Because you've created an infinite loop, you don't want to load it in the browser and have it churn forever. Instead you can use the PHP command line tool and run it in a terminal. Make sure the PHP binary is added to your PATH or browse to its location on your machine. Then you can run the application you just created and display all of the text to the terminal. It will run until you kill it. You may have to run it using the sudo command because you're trying to listen on a socket lower than 1024. Here's what my terminal looked like when I executed the command:


RYAN-STEWARTs-MacBook-Pro:socket_demo rstewart$ sudo php socket_authentication.php
Password:
Socket created.
Socket bound to port 843.
Listening on the socket.

 

Handling requests on the main socket

Before moving to the Flash Player side, you’ll need to set up the code you want to run after you get approval from the policy file to send data over a socket. To set up the socket, use the same code used above but with a different port (in this case, port 1740).


<?php

create_connection('localhost',1740);

function create_connection($host,$port)
{
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

if (!is_resource($socket)) {
echo 'Unable to create socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo "Socket created.\n";
}

if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo "Set options on socket.\n";
}

if (!socket_bind($socket, $host, $port)) {
echo 'Unable to bind socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo "Socket bound to port $port.\n";
}


if (!socket_listen($socket,SOMAXCONN)) {
echo 'Unable to listen on socket: ' . socket_strerror(socket_last_error());
} else {
echo "Listening on the socket.\n";
}

while (true)
{
$connection = @socket_accept($socket);

if($connection)
{
echo "Client $connection connected!\n";
send_data($connection);
} else {
echo "Bad connection.";
}
}
}

 

This should all look familiar from the previous example. One main difference is that I've put the connection logic into its own function called create_connection(), which takes a hostname and a port. The other difference is that instead of sending policy file information, it calls a function named send_data() after a connection is successfully established. This send_data() function is going to generate a series of stock quotes and send them to the client using the same socket_write() method used in the previous example.


function send_data($connection)
{
echo $connection;

// Create a number between 30 and 32 that will be our initial stock price.
$stock_price = rand(30,32);

while (true)
{
socket_write($connection,"$stock_price\n",strlen("$stock_price\n"));
sleep(2);

// Generate a random number that will represent how much our stock price
// will change and then make that number a decimal and attach it to the
// previous price.
$stock_offset = rand(-50,50);
$stock_price = $stock_price + ($stock_offset/100);
echo "$stock_price\n";
}
}

 

Save this as socket_quotes.php in the directory you placed socket_authentication.php. Run it the same way; you should see the following output when you run it in your terminal:


RYAN-STEWARTs-MacBook-Pro:socket_demo rstewart$ php socket_quotes.php
Socket created.
Set options on socket.
Socket bound to port 1740.
Listening on the socket.

 

Before you move to the Flex code, make sure both of these files are running in separate terminal windows. When Flash Player connects, it will first run the socket_authentication.php code as it checks the policy file and then run the code in socket_quotes.php to start getting data.

 

Creating the Flex application

Open Flash Builder 4 and create a new Flex project. Set the server type to None/Other and use the default project location (or another location if you wish). 

The first step is to add the layout and component code. The application is going to be a basic stock tracker that shows the latest stock price, a list of old stock prices, and a chart that displays the stock price over time. All of the components are going to be in MXML.


<s:Button id="btnConnected" click="btn_clickHandler(event)" label="Connect" height="26" width="90"  x="287" y="60"/>
<s:Button id="btnQuit" click="btnQuit_clickHandler(event)" label="Quit" height="26" width="90" x="385" y="60"/>
<s:RichText id="lblPrice" color="0xFFFFFF" fontFamily="Myriad Pro" fontSize="60" kerning="on" lineHeight="120%" textAlign="center" whiteSpaceCollapse="preserve" x="129" y="160">
</s:RichText>
<s:List id="list" x="377" y="109" dataProvider="{arrStockData}" labelField="currentValue" />
<mx:LineChart x="40" y="263" id="linechart1"
width="325" height="212" showDataTips="true"
dataProvider="{arrStockData}" color="#000000">
<mx:verticalAxis>
<mx:LinearAxis maximum="35" minimum="25" displayName="Price" />
</mx:verticalAxis>
<mx:series>
<mx:LineSeries displayName="Value" yField="currentValue" form="curve" />
</mx:series>
</mx:LineChart>
 

The code above defines two buttons, one that will make the connection and another that closes it. Next, it defines a RichText field that will show the current stock price. Following that are the list and the chart, which will both use an array as the dataProvider. In Flex you can bind data to components using the {} notation; when that data changes all of the visual components that are bound to it are updated as well. In this case, the list and chart are bound to an array, arrStockData, so that any changes made to the array will be shown in the chart and the list. As a result, you can just update the data in the array and the entire application will show the new data.

With the components created, you’re ready to move on to the script part of the application. This is where you create the socket connection and also handle any incoming data. You’ll start by defining the array and the socket itself and then create the click handler for the Connect button. All ActionScript 3 code has to be in an <fx:Script> block, so place the code above the components.


<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;

public var socket:Socket;

[Bindable]
protected var arrStockData:ArrayCollection = new ArrayCollection();

protected function btn_clickHandler(event:MouseEvent):void
{
socket = new Socket();
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
socket.addEventListener(IOErrorEvent.IO_ERROR,onIOError);
socket.addEventListener(Event.CONNECT,onConnect);
socket.addEventListener(ProgressEvent.SOCKET_DATA,onProgressEvent);
socket.connect("127.0.0.1",1740);
}
 

The [Bindable] tag above the array definition lets you bind components to that data. When the Connect button is clicked, btn_clickHandler()creates the socket connection. Creating a socket connection in Flex is pretty easy. Simply create an instance of the Socket class and then call the connect() method on it with a host and a port. You’ll want to set up event listeners to take some action when various things happen on the socket. I’ve included some below. For the error events I just trace out the error so the user can see it. When the user connects, onConnect() changes the label on the Connect button to "Connected" and disables it. The most important function is onProgressEvent(), which is called when socket data is received. That function handles the data that is sent by the socket server.

          protected function onSecurityError(event:SecurityErrorEvent):void
{
Alert.show(event.text);
}

protected function onIOError(event:IOErrorEvent):void
{
Alert.show(event.text);
}

protected function onConnect(event:Event):void
{
btnConnected.label = "Connected";
btnConnected.enabled = false;
}

protected function onProgressEvent(event:ProgressEvent):void
{
var data:String = socket.readUTFBytes(socket.bytesAvailable);
if(Number(data) < Number(lblPrice.text))
{
lblPrice.setStyle("color","#ff0000");
} else {
lblPrice.setStyle("color","#00ff00");
}

lblPrice.text = data;
var lastValue:Number = arrStockData.length ? arrStockData[0].currentValue : 0;
arrStockData.addItemAt({currentValue:data,lastValue:lastValue},0);
trace('data received');
}

protected function btnQuit_clickHandler(event:MouseEvent):void
{
socket.close();
btnQuit.enabled = false; }
]]> </fx:Script>
 

The onProgressEvent() function is responsible for several tasks. Most importantly, it calls the readUTFBytes() method on the socket and saves that data so it can work with it. The Socket class in Flash Player supports sending text or binary data across the socket so you can use one of several "read" methods depending on what kind of data you need to read. When new data is received, onProgressEvent()stores the previous value as lastValue (using 0 if this is the first data point). With that information it adds a new object to the array, recording the currentValue as well as the lastValue.This will be used as part of the skinned project for the ItemRenderer on the List. The logic compares the current value with the previous value to determine if the stock has gone up or down. If it has gone down, the setStyle() method is called to turn the stock quote red, and if it goes up, it is set to green. The last function, btnQuit_clickHandler(), is called when the Close button is clicked. It calls the close() method on the socket to disconnect it.

And that's all there is to it. If your two socket applications are running in the terminal you can run this application. You should start to see near real-time stock quotes being sent to your application and the list and chart will update automatically.

You can use a new feature in Flex 4 that lets you more easily create custom-looking applications and put a skin on it to make it look a bit more like a real stock dashboard:

 

 

You can grab all of the files here. You should now be well on your way to creating your own socket-based applications in PHP and Flex.

AttachmentSize
Figure1.png107.38 KB
Published at DZone with permission of its author, Ryan Stewart.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Dennis Biron replied on Wed, 2010/02/24 - 12:48pm

Ryan, Great article ... It's a good start for me. Once I stop the flex app... I noticed that the socket_quotes service returns warnings "unable to write to socket". It does make sense since the resource is no longer available, the connection is closed. My question is: once the socket was opened by the client, is there a way to recover when the client (Flex) is closed? thanks....

Ryan Stewart replied on Thu, 2010/02/25 - 7:23pm in response to: Dennis Biron

I think the problem might be that I'm calling the data through another function. If all of the "send data" logic is inside the main while(true) loop that accepts connections, it should work just fine. The policy file example code works that way and doesn't have a problem when a client disconnects. =Ryan ryan@adobe.com

Chris Wagoner replied on Thu, 2010/08/26 - 8:50am

So this is a great example and i am very thankful. the question have now is what is the scalability to something like this? i have a shared web server at the moment(low start up cost), and i am looking to send live data to my flex clients that will be hosted on many web sites(hopefully/ideally). To my understanding(still reading up) there is overhead for each client as once connecting to the socket there is an open session? is this accurate? even is all clients are connecting to the same socket server? Next time your on long island let me know and ill get you that beer ;)

Chris Wagoner replied on Thu, 2010/08/26 - 9:05am

Also on the flex side of things i get this warning warning: unable to bind to property 'currentValue' on class 'Object' (class is not an IEventDispatcher) when data is received. is that to be expected?

Poncho Mendez replied on Thu, 2010/09/30 - 5:31am

I think this is a great tutorial, and so easy to follow, ive just done all correct, but im getting the same socket error under windows xp and xampp shell console, do you know why is this erro, and how it could be fixed? thank you so much.

Nikola Nikolov replied on Thu, 2010/10/21 - 4:44am

Any live examples of this?

Julien Benoit replied on Fri, 2011/05/06 - 4:57am

Here a great tutorial, very easy to figure out, thank you very much Ryan for your contribution.

Carla Brian replied on Fri, 2012/04/06 - 9:11am

Sockets are flexible and sufficient. Efficient socket based programming can be easily implemented for general communications. - Paul Perito

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.