Передаем больше 40 килобайт по LocalConnection

в 14:40, , рубрики: Action Script, actionscript 3.0, 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);

Передаем больше 40 килобайт по LocalConnection - 1
Результат работы приемника

Передаем больше 40 килобайт по LocalConnection - 2
Результат отправки объекта

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

Автор: TALUKS

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js