Статьи

Общение с Flex и PHP через сокеты

Существует несколько способов связи между Flex и PHP, но один из наиболее интересных — через сокеты. Связь через сокет позволяет разработчикам создавать связь практически в реальном времени, передавая информацию непосредственно клиенту. В этой статье вы познакомитесь с основами использования PHP в качестве сервера сокетов и подключения его к приложению Flex.

Сокеты — это практичный и эффективный способ отправки данных с сервера на клиент. В традиционной модели HTTP клиент отправляет запрос на сервер, сервер обрабатывает его, клиент получает ответ и, наконец, отключается от сервера. Для этого HTTP требует, чтобы клиент отправил значительное количество дополнительной информации, включая информацию о том, как долго поддерживать соединение, информацию о безопасности и разрешениях, а также информацию, которая идентифицирует запрос. При отправке множества пакетов данных за короткий промежуток времени, как вы могли бы сделать с биржевыми котировками или приложением чата, вашему приложению придется повторно подключаться несколько раз и выполнять несколько обменов назад и вперед с сервером, отправляя все эти дополнительные информация каждый раз, чтобы получить данные.Каждая транзакция может занять в два раза больше времени, так как клиент отправляет запрос и ожидает ответа для его получения. Это может быть проблематично для приложений, которые нуждаются в мгновенной или почти в реальном времени обратной связи. К счастью, есть простое решение этой проблемы: сокеты.

В отличие от HTTP, когда вы используете сокеты, приложение делает один запрос к серверу; сервер открывает соединение и поддерживает его, чтобы он мог передавать данные клиенту в любое время. И клиент, и сервер связываются с сокетом, чтобы они могли прослушивать любые изменения в информации, и клиент и сервер могут отправлять информацию через этот сокет. Это означает, что клиент будет получать любые данные, отправленные сервером через сокет, без необходимости запроса. В результате вы можете сократить время получения данных вдвое, и ваше приложение сможет отправлять и получать эти данные практически в реальном времени.

Чтобы использовать сокеты с Flash Player, вам нужно выполнить пару дополнительных шагов. В целях безопасности Flash Player требует, чтобы файл политики находился на сервере, к которому будет подключаться клиент. В Центре разработчиков Adobe есть статья
это охватывает некоторые из обоснований этого требования и шаги, необходимые для его реализации в деталях. Перед тем как Flash Player отправляет запрос на сокет, он сначала устанавливает другое соединение с сокетом на конкретном порту (843), а затем ожидает, что ответом будет файл политики, содержащий информацию о том, кому разрешено подключаться и к каким портам Flash Player может устанавливать подключения. , В статье Developer Connection выше объясняется, что должны содержать файлы политики. Файл политики, используемый в этой статье, приведен ниже; он сохраняется в том же каталоге, что и ваш PHP-код, как 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>

 

На сервере это означает, что вы должны программировать для двух случаев; первый для обработки подключения Flash Player для получения файла политики, а второй для обработки подключения Flash Player для обмена данными. Для целей этого урока я разделил эти два случая на отдельные файлы.

Я расскажу о PHP-коде, чтобы сначала предоставить файл политики.

 

Обработка запросов файла политики

Сначала вам нужно получить содержимое файла flashpolicy.xml, а затем настроить сокет. Функция socket_create принимает три параметра, которые позволяют вам точно определить, какой протокол использовать при создании сокета.

<?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";
}
?>

 

После того, как вы его создали, вам нужно привязать к хосту и конкретному порту. В этом случае, потому что Flash Player будет запрашивать данные у порта 843, то есть порта, который нужно использовать. После этапа связывания код прослушивает в связанном сокете любые данные, которые могут быть отправлены с клиента.

//  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";
}

 

При этом сокет должен быть настроен правильно, и вы можете начать отправку и получение данных. Связь через сокет сильно отличается от модели запрос-ответ, с которой вы, возможно, знакомы, и в результате код может показаться нелогичным. В отличие от типичной страницы PHP, которая загружается в браузер, когда браузер запрашивает ее, связь с сокетом является непрерывной. В результате вы никогда не захотите закончить «загрузку» страницы. Для достижения этой цели, остальная часть коды помещаются в то время как цикл , который продолжает работать, принимая любые соединения , которые приходят в. 

В этом цикле while вам необходимо создать соединение, принимая все попытки подключения к сокету. Если соединение установлено успешно, у вас есть объект соединения, который вы можете затем прочитать или записать.

// 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.";
}

 

Если проигрыватель Flash Player успешно подключается, то первое, что он записывает в сокет, — это запрос к серверу файла политики. Чтобы обработать это на сервере, необходим вызов метода socket_read , чтобы получить данные, отправляемые клиентом, чтобы их можно было обработать. Если серверный код получает строку «<policy-file-request />», за которой следует нулевой символ, он отправляет данные из файла политики, в противном случае он просто закрывает соединение.

     // 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;
}

 

Наконец, вам необходимо отправить требуемый файл политики Flash Player , а затем выйти из то время как цикл. В этом примере файл политики определяет широко открытую политику, которая позволяет любому подключаться, но обычно вы хотите ограничить имена хостов и порты, чтобы контролировать, как Flash Player может подключаться к вашему серверу сокетов.

     // 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);
}

 

Теперь у вас есть код сервера, который позволит Flash Player подключиться к сокету и запросить файл политики. Сохраните этот файл как socket_authentication.php. Запуск этого кода отличается от запуска его на веб-сайте. Поскольку вы создали бесконечный цикл, вы не хотите загружать его в браузер и иметь постоянный отток. Вместо этого вы можете использовать инструмент командной строки PHP и запустить его в терминале. Убедитесь, что бинарный файл PHP добавлен в вашу переменную PATH или перейдите к его местоположению на вашем компьютере. Затем вы можете запустить только что созданное приложение и отобразить весь текст на терминале. Он будет работать, пока ты его не убьешь. Возможно, вам придется запустить его с помощью команды sudo, потому что вы пытаетесь прослушивать сокет ниже 1024. Вот как выглядел мой терминал, когда я выполнил команду:

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

 

Обработка запросов на основном сокете

Прежде чем перейти на сторону Flash Player, вам нужно настроить код, который вы хотите запустить, после получения одобрения из файла политики для отправки данных через сокет. Для настройки сокета используйте тот же код, что и выше, но с другим портом (в данном случае это порт 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.";
}
}
}

 

Все это должно выглядеть знакомым из предыдущего примера. Одно из основных отличий заключается в том, что я поместил логику соединения в свою собственную функцию create_connection (), которая принимает имя хоста и порт. Другое отличие состоит в том, что вместо отправки информации о файле политики он вызывает функцию send_data () после успешного установления соединения. Эта функция send_data () будет генерировать серию биржевых котировок и отправлять их клиенту, используя тот же метод socket_write (), который использовался в предыдущем примере.

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";
}
}

 

Сохраните это как socket_quotes.php в каталоге, в который вы поместили socket_authentication.php. Запустите его так же; вы должны увидеть следующий вывод при запуске в терминале:

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.

 

Прежде чем перейти к коду Flex, убедитесь, что оба эти файла работают в отдельных окнах терминала. Когда Flash Player подключается, он сначала запускает код socket_authentication.php, когда проверяет файл политики, а затем запускает код в socket_quotes.php, чтобы начать получать данные.

 

Создание приложения Flex

Откройте Flash Builder 4 и создайте новый проект Flex. Установите тип сервера None / Other и используйте расположение проекта по умолчанию (или другое расположение, если хотите). 

Первый шаг — добавить макет и код компонента. Приложение будет основным средством отслеживания акций, которое показывает последнюю цену акций, список старых цен акций и диаграмму, которая отображает цену акций с течением времени. Все компоненты будут в 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>

 

Приведенный выше код определяет две кнопки: одну для установления соединения и другую для ее закрытия. Затем он определяет поле RichText, в котором будет отображаться текущая цена акций. Далее следуют список и диаграмма, которые будут использовать массив в качестве dataProvider. Во Flex вы можете связать данные с компонентами, используя нотацию {} ; когда эти данные изменяют все визуальные компоненты, которые с ними связаны, также обновляются. В этом случае список и диаграмма связаны с массивом arrStockData , поэтому любые изменения, внесенные в массив, будут отображаться на диаграмме и в списке. В результате вы можете просто обновить данные в массиве, и все приложение покажет новые данные.

Создав компоненты, вы готовы перейти к части сценария приложения. Здесь вы создаете сокет-соединение и обрабатываете любые входящие данные. Вы начнете с определения массива и самого сокета, а затем создадите обработчик щелчка для кнопки Connect. Весь код ActionScript 3 должен находиться в блоке <fx: Script> , поэтому разместите код над компонентами.

<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);
}

 

[Bindable] тег выше определения массива позволяет вам привязку компонентов к этим данным. Когда кнопка Connect нажата, btn_clickHandler () создает соединение с сокетом. Создать сокет-соединение во Flex довольно просто. Просто создайте экземпляр класса Socket и затем вызовите для него метод connect () с хостом и портом. Вы захотите настроить прослушиватели событий на выполнение определенных действий, когда в сокете происходят разные вещи. Я включил некоторые ниже. Для событий ошибок я просто отслеживаю ошибку, чтобы пользователь мог ее увидеть. Когда пользователь подключается, onConnect () меняет метку на кнопке Connect на «Connected» и отключает ее. Наиболее важной функцией является onProgressEvent (), который вызывается при получении данных сокета. Эта функция обрабатывает данные, отправляемые сервером сокетов.

          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>

 

Функция onProgressEvent () отвечает за несколько задач. Самое главное, что он вызывает метод readUTFBytes () для сокета и сохраняет эти данные, чтобы он мог работать с ним. Класс Socket в Flash Player поддерживает отправку текстовых или двоичных данных через сокет, поэтому вы можете использовать один из нескольких методов «чтения» в зависимости от того, какие данные вам нужно прочитать. При получении новых данных onProgressEvent () сохраняет предыдущее значение как lastValue (используя 0, если это первая точка данных). С этой информацией он добавляет новый объект в массив, записывая currentValue, а также lastValue.
Это будет использоваться как часть проекта скинов для ItemRenderer в Списке. Логика сравнивает текущее значение с предыдущим значением, чтобы определить, выросла или снизилась акция. Если он понизился, вызывается метод setStyle (), чтобы сделать котировку акций красной, а если она поднимется, то станет зеленой. Последняя функция, btnQuit_clickHandler (), вызывается при нажатии кнопки «Закрыть». Он вызывает метод close () для сокета, чтобы отключить его.

И это все, что нужно сделать. Если в терминале работают два ваших сокет-приложения, вы можете запустить это приложение. Вы должны начать видеть котировки акций в реальном времени, отправленные в ваше приложение, и список и график обновятся автоматически.

Вы можете использовать новую функцию в Flex 4, которая позволяет более легко создавать настраиваемые приложения и накладывать на них обложку, чтобы она выглядела немного больше, чем на обычной панели инструментов:

 

 

Вы можете взять все файлы здесь . Теперь вы должны быть готовы к созданию собственных приложений на основе сокетов в PHP и Flex.