Communicating with Flex and PHP over Sockets
Join the DZone community and get the full member experience.
Join For FreeThere 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.
Opinions expressed by DZone contributors are their own.
Trending
-
Azure Virtual Machines
-
How to Use an Anti-Corruption Layer Pattern for Improved Microservices Communication
-
Top 10 Pillars of Zero Trust Networks
-
Java Concurrency: Condition
Comments