...

среда, 17 декабря 2014 г.

[Из песочницы] Передаем больше 40 килобайт по LocalConnection

С ростом Flash проекта состоящего из раздельных .swf, соединенных между собой через LocalConnection, может возрасти потребность в размерах передаваемых данных. К примеру, это может быть обмен данными присланных сообщений между игрой и отдельным приложением сообщений.

Проблемы начинаются когда передаваемых данных становится больше 40 Кб, появляется ошибка и приложение перестает нормально работать.


Напомню, что объем данных, которые можно передать как параметры методом send, ограничен 40 килобайтами.


Есть несколько выходов из такой ситуации:



  1. Использовать Local Shared Objects;

  2. Передавать данные с помощью JavaScript;

  3. Передавать данные по LocalConnection разбив на части.


Первые два пункта требуют больших изменений чем последний. Поэтому с минимальными затратами реализуем передачу данных по частям которые будут меньше 40 Кб.


Структура пакета:



  1. unsigned int — длина данных всего сообщения;

  2. unsigned int — длина данных объекта;

  3. данные сериализованного объекта.


Что бы правильно собрать все данные напишем обертку для клиента LocalConnection:




public class ExtendedClient
{
private var receivedData:ByteArray = new ByteArray();
private var length:int = -1;
/** Пользовательский класс - обработчик*/
private var client:Object;
public function ExtendedClient(client:Object)
{
this.client = client;
}
/**
* Метод удаленного вызова.
* @param packet - входящие данные.
*/
public final function reciverLocalConnection(packet:ByteArray):void
{
// Читаем длину всего сообщения
if(length == -1) length = packet.readUnsignedInt();

packet.readBytes(receivedData, receivedData.length, packet.length - packet.position);
// Если необходимое количество данных принять
if(receivedData.length == length)
{
// Обрабатываем полученные данные
deserialization(receivedData);
receivedData = new ByteArray();
length = -1;
}
}
/**
* Десериализаия данных.
* @param receivedData - принятые данные.
*/
private function deserialization(receivedData:ByteArray):void
{
var parameters:Array = new Array();
var temp:ByteArray = new ByteArray();
while(receivedData.position != receivedData.length)
{
receivedData.readBytes(temp, 0, receivedData.readUnsignedInt() - 4);
parameters.push(temp.readObject());
temp.clear();
}
try
{ // Отправляем данные в запрашиваемый мтод
(client[parameters[0]] as Function).apply(this, parameters.slice(1));
}catch(error:ReferenceError)
{
trace("Error:", error.message);
}
}
}




Дальше расширяем стандартный класс LocalConnection, передачу данных будем осуществлять с учетом, размера всех аргументов передаваемых в метод send:

public class ExtendedLocalConnect extends LocalConnection
{
/** Максимальный размер блока данных*/
public static const BLOCK_WEIGHT:uint = 40000;

private var _complete:Boolean = true;
public function ExtendedLocalConnect()
{
super();
}
/**
* Метод отправки данных.
* @param localConnectionName - имя подключения;
* @param args - аргументы, первым должено идти название вызываемого метода.
*/
public final function write(localConnectionName:String, ... args):void
{
_complete = false;
var bytes:ByteArray = new ByteArray();
// Оставляем место для длины данных сообщения
bytes.writeUnsignedInt(0);
// Сериализуем все аргументы
for(var i:int = 0, n:int = args.length; i < n; i++)
{
var startPosition:int = bytes.position;

bytes.writeUnsignedInt(0);
bytes.writeObject(args[i]);
bytes.position = startPosition;
// Записываем длину данных объекта
bytes.writeUnsignedInt(bytes.length - startPosition);
bytes.position = bytes.length;
}
bytes.position = 0;
//Записываем длину данных сообщения
bytes.writeUnsignedInt(bytes.length - 4);
bytes.position = 0;

var bytesLength:int = bytes.length;

var currentPosition:int = 0;
var currentLength:int = bytesLength;
while(currentLength > 0)
{
var packageLength:int = (currentLength > BLOCK_WEIGHT)? BLOCK_WEIGHT: currentLength;

var packet:ByteArray = new ByteArray();
// Формируем пакет данных
bytes.readBytes(packet, 0, packageLength);
currentPosition += packageLength;
currentLength = bytesLength - currentPosition;

if(!currentLength)
_complete = true;
// Отправляем пакет
send(localConnectionName, "reciverLocalConnection", packet);
}
bytes.clear();
}
public function set target(extendedClient:ExtendedClient):void
{
client = extendedClient;
}

public function get complete():Boolean
{
return _complete;
}
}


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

Пример использования:



var extendedLocalConnection:ExtendedLocalConnect = new ExtendedLocalConnect();
var client:ExtendedClient = new ExtendedClient(this);
extendedLocalConnection.target = client;
// Для приемника
extendedLocalConnection.connect("myConnection");
// Отправка данных
extendedLocalConnection.write("myConnection", "myFunction", data);




Результат работы приемника




Результат отправки объекта


Таким образом мы можем легко заменить стандартный LocalConnection и обойти ограничение на передачу данных. А в случае когда необходимо использовать и стандартные методы передачи данных, то можно расширить класс ExtendedClient и описать дополнительные методы вызова.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Want something else to read? How about 'Grievous Censorship' By The Guardian: Israel, Gaza And The Termination Of Nafeez Ahmed's Blog


Комментариев нет:

Отправить комментарий