Февраль 2011

Руководство по actionscript. часть 6, стр. 045

Отметим, что поведение сервера FileSender полностью автоматизировано: клиенту не требуется запрашивать элемент с сервера или отправлять подтверждение о получении элемента в каком-либо виде. Данная архитектура позволяет сконцентрироваться исключительно на процессе передачи элемента.

В листинге 28.12 представлен исходный код на языке Java для сервера FileSender, любезно предоставленный Дереком Клейтоном (Derek Clayton) для этой книги.

Листинг 28.12. Сервер FileSender

import java. net. ServerSocket:

import java. net. Socket;

import java. io. IOException;

import java. io. InputStream;

import java. io. File;

import java. io. FilelnputStream;

import java. iо. BufferedOutputStream;

/**

* FileSender — это простой сервер, который принимает сокетное соединение

* и передает файл, после чего соединение закрывается. *

* Использование: java FileSender [порт] [имя_файла]

* [порт] = порт, на котором сервер будет ожидать подключения (на всех

* локальных IP-адресах)

* [имя_файла] = путь к передаваемому файлу *

*/

public class FileSender implements Runnable { private int port; private File file; private String filename; private ServerSocket server; private Thread thisThread; private byte[] bytes;

public FileSender(int p, String f) { port = p; filename = f:

}

public void start( ) { InputStream is = null; try {

// — читаем файл в наш массив байтов

file = new File(filename):

is = new FilelnputStream(file);

bytes = new byte[(int)file. length( )+l];

int offset = 0;

int byteRead = 0;

while (offset < bytes.length

&& (byteRead=is. read(bytes, offset, bytes. length-offset)) >= 0) { offset += byteRead;

}

bytes[bytes.1ength-1] = 4;

// — создаем объект ServerSocket server = new ServerSocket(port); } catch (Exception e) { e. printStackTrace( ); System. exit(1); } finally {

if (is!= null) { try {

is. close( ); } catch (Exception e) { e. printStackTrace( ); System. exit(l);

}

}

}

// — запускаем объект Thread, который будет принимать подключения thisThread = new Thread(this); thisThread. start( );

}

public void run( ) {

// — пока сервер активен… while (thisThread!= null) {

BufferedOutputStream ps = null;

Socket socket = null ;

try {

// — …принимаем сокетные соединения

// (выполнение потока блокируется, пока не будет

// установлено соединение)

socket = server. accept( );

// — создаем выходной поток

ps = new BufferedOutputStream(socket. getOutputStream( ));

// — записываем байты и закрываем соединение ps. write(bytes); ps. close( );

ps = null; socket. close( ); socket = null; } catch(Exception e) { thisThread = null; e. printStackTrace( ): } finally {

if (ps!= null) { try {

ps. close( ); } catch (IOException e) { e. printStackTrace( ); System. exit(1);

}

}

if (socket!= null) { try {

socket. close( ); } catch (IOException e) {

e. printStackTrace( );

System. exit(1);

}

}

}

}

// — освобождаем ресурсы, занимаемые сервером if (server!= null) { try {

server. close( ); } catch (IOException e) { e. printStackTrace( ); System. exit(l);

}

}

}

public final static void main(String [] args) { // — проверяем, все ли аргументы указаны if (args. length!= 2) {

System. out. printlnC’usage: java FileSender [port] [file]«); System. exit(l);

}

try {

// — преобразуем аргументы к их соответствующему типу int port = Integer. parselnt(args[0]); String filename = args[l];

// — создаем и запускаем объект FileSender

// (который будет выполняться // в своем собственном потоке) FileSender fs = new FileSender(port, filename); fs. start< ); catch (exception e) { e.printstacktrace( ); system.exit(l);

S*4

Исходный код для сервера FileSender можно загрузить по адресу http://moock. org/eas3/ examples.

Руководство по actionscript. часть 6, стр. 046

Чтобы запустить сервер FileSender, мы вводим следующую команду для среды выполнения Java:

java FileSender порт имяФайла

Здесь порт — это порт, на котором сервер будет принимать соединения, а имяФайла — имя файла, передаваемого сервером FileSender любому подключившемуся клиенту. Например, чтобы запустить сервер на порте 3000 и сконфигурировать его на отправку файла с именем photo. jpg, мы вводим следующую команду для среды выполнения Java:

java FileSender 3000 photo. jpg

Информацию об ограничениях безопасности, касающихся сокетных соединений, можно найти в гл. 19.

Клиентская часть: получение элемента

Мы только что рассмотрели код для пользовательского сервера, написанного на языке Java, который автоматически отправляет указанный файл любому подключившемуся клиенту. Теперь создадим соответствующего клиента на языке ActionScript, подключаемого к серверу и получающего файл.

В отличие от многих традиционных языков программирования, система сокетных взаимодействий языка ActionScript построена полностью на событиях.

«г»

I В ActionScript невозможно приостановить выполнение программы, ожидая появления м$ d * Данных в сокете. Иными словами, сокетные операции языка ActionScript являются асинхронными, а не синхронными.

Руководство по actionscript. часть 6, стр. 047

В языке ActionScript данные могут быть прочитаны из сокета только после того, как возникнет событие Progres sEvent. S ОСКЕ T_D AT А. Данное событие сообщает о том, что клиенту доступен некий произвольный объем новых данных для чтория. Однако как только операция чтения новых данных будет завершена, клиент должен снова дожидаться возникновения следующего события ProgressEvent. SOCKET_DATA, чтобы прочитать дополнительные данные из сокета. Рассмотрим общее описание процесса.

1. Клиент подключается к сокету.

2. Сокет получает некоторые данные.

3. Возникает событие ProgressEvent. SOCKET_DATA.

4. Клиент считывает все доступные данные.

5. Сокет получает дополнительные данные.

6. Возникает событие ProgressEvent. SOCKET_DATA.

7. Клиент считывает все доступные данные.

8. Повторение шагов 5-7 до тех пор, пока не будет закрыт сокет.

Объем данных, которые появляются в сокете с возникновением каждого события ProgressEvent. SOCKET_DATA, полностью произволен. Зачастую данные, доступные клиенту при возникновении события ProgressEvent. SOCKET_DATA, составляют лишь часть большого целого. Таким образом, вы должны проявлять особую осторожность, выполняя ручную сборку всех необходимых данных перед их обработкой. Клиент может получить данные некоторого изображения, скажем, в трех сегментах, каждый из которых вызывает событие Progres sEvent. SOCKET_DATA. Перед тем как обработать все изображение целиком, клиентский код должен собрать эти три сегмента вместе.

Руководство по actionscript. часть 6, стр. 048

Чтобы собрать небольшие сегменты данных в одно целое, клиент должен вручную добавлять каждый сегмент во временный массив байтов (то есть в «буфер байтов») по мере загрузки этого целого. «Целым» может быть файл, объект, готовая инструкция, почтовое сообщение, сообщение чата или любая другая логическая структура данных, которая должна обрабатываться как единый элемент. Всякий раз, когда появляется новый сегмент, клиент проверяет, не завершена ли загрузка всего целого. Если да, то клиент приступает к его обработке. Если нет, то клиент ожидает появления дополнительных данных.

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

Посмотрим, как все это будет выглядеть в коде. Наш простой клиент, написанный на ActionScript, состоит из одного класса DisplayAssetLoader. Чтобы получить и отобразить элемент, отправляемый сервером FileSender, класс DisplayAssetLoader должен выполнить такую последовательность действий.

1. Создать объект Socket.

2. Зарегистрировать объект DisplayAssetLoader для событий объекта Socket.

3. Использовать объект Socket для подключения к серверу.

4. Когда через сокет будут получены новые бинарные данные, поместить эти данные во временный буфер.

5. Когда сокет отсоединится, использовать метод экземпляра loadBytes ( ) класса Loader, для того чтобы загрузить бинарные данные из временного буфера в объект Loader.

6. Отобразить объект загруженного элемента на экране.

В листинге 28.13 представлен весь код целиком для класса DisplayAssetLoader. Ключевые возможности класса DisplayAssetLoader обсуждаются после листинга; незначительные детали описываются в виде комментариев к коду.

Руководство по actionscript. часть 6, стр. 049

Листинг 28.13. Класс DisplayAssetLoader

package { import flash. display.*; import flash. events.*; import flash. net.*; import flash. text.*; import flash. utils.*;

public class DisplayAssetLoader extends Sprite { // Константа, представляющая ASCII-символ // «завершение передачи» public static const EOT:int = 4; // Объект TextField, отображаемый на экране, // в который выводятся // статусные сообщения private var statusField.-TextField;

// Объект сокета, через который будет устанавливаться соединение private var socket:Socket;

// Буфер байтов, в который по мере загрузки будут помещаться бинарные // данные элемента

private var buffer:ByteArray = new ByteArray( );

// Объект Loader, используемый для генерации элемента из загруженных

// бинарных данных

private var loader:Loader;

// Конструктор класса

public function DisplayAssetLoader ( ) {

// Создаем объект TextField для отображения статусных сообщений

statusField = new TextField( );

statusField. border = true;

statusField. background = true;

statusField. width = statusField. height = 350;

addChild(statusField);

// Создаем объект сокета socket = new Socket( );

// Регистрируем приемники для событий сокета socket. addEventLi stener(Event. CONNECT, connectListener); socket. addEventLi stener(Event. CLOSE, closeLi stener); socket. addEventLi stener(ProgressEvent. S0CKETJ3ATA,

socketDataListener); socket. addEventListener(IOErrorEvent.10 ERROR, ioErrorListener);

// Сообщаем пользователю, что сейчас мы попытаемся подключиться // к сокету

out(«Attempting connection…»);

// Пытаемся подключиться к сокету try {

socket. connectClocalhost». 3000): } catch (e:Error) { outC’Connection problem!\n»); out(e. message):

}

// Обрабатывает события подключения к сокету private function connectListener (e:Event):void { out(«Connected! Waiting for data…»);

}

// Обрабатывает вновь полученные данные private function socketDataListener (e:ProgressEvent):void { out(«New socket data arrived.»);

// Когда появляются новые байты, помещаем их в буфер для дальнейшей // обработки

socket. readBytes(buffer, buffer. length, socket. bytesAvai1able);

}

// Обрабатывает события отключения сокета. Когда происходит отключение, // пытаемся сгенерировать отображаемый элемент из загруженных байтов, private function closeListener (e:Event):void {

// Сначала проверяем, был ли получен весь элемент целиком…

Руководство по actionscript. часть 6, стр. 050

// Обращаемся к последнему байту в буфере

buffer. position = buffer. length — 1;

var lastByte;int = buffer. readUnsignedByte( );

// Если байт «завершение передачи» отсутствует,

// значит, бинарные данные элемента загружены не полностью,

// поэтому элемент не создаем

if (lastByte!= DisplayAssetLoader. EOT) { return;

}

// Все в порядке, мы можем благополучно сгенерировать элемент из // загруженных байтов. Последний байт в буфере не является частью // элемента, поэтому отбрасываем его. buffer. length = buffer. length — 1;

// Теперь создаем объект Loader, который сгенерирует элемент // из загруженных байтов loader = new Loader( );

// Генерируем элемент из загруженных байтов 1oader.1oadBytes(buffer);

// Ожидаем завершения процесса инициализации элемента 1oader. contentLoaderlnfo. addEventLi stener(Event. INIT,

assetlnitListener);

}

// Помещает элемент на экран после завершения процесса его инициализации private function assetlnitListener (e:Event):void {

addChi1d(1oader. content);

outCAsset initialized.»);

}

// Обрабатывает ошибки ввода/вывода private function ioErrorListener (e:IOErrorEvent):void { out(«I/0 Error: » + e. text);

}

// Выводит статусные сообщения на экран и на отладочную консоль private function out (msg:*):void { trace(msg);

statusField. appendText(msg + «\n»);

}

}

}

Рассмотрим три основные части класса DisplayAssetLoader: создание и подключение к сокету, помещение байтов в буфер и создание отображаемого элемента из загруженных байтов.

Руководство по actionscript. часть 6, стр. 051

Создание и подключение к сокету

Для установки сокетного соединения и управления им мы используем экземпляр класса Socket, который присваиваем закрытой переменной socket:

socket = new Socket( );

Чтобы подключиться к сокету, мы используем метод экземпляра connect ( ) класса Socket. Однако поскольку сокетные соединения потенциально могут генерировать ошибки безопасности и ошибки ввода/вывода, мы помещаем вызов метода connect ( ) в блок try/catch:

try {

socket. connectC’localhost», 3000); } catch (e:Error) { out(«Connection problem!\n»); out(e. message);

}

Помещение байтов в буфер

Как мы уже знаем, при получении новых данных через сокет среда Flash выполняет диспетчеризацию события ProgressEvent. SOCKET_DATA. Получателем данного события является объект Socket, получивший эти данные. Таким образом, чтобы получать уведомления о появлении новых данных, мы регистрируем в объекте socket приемник для события ProgressEvent. SOCKET_DATA, как показано в следующем коде:

socket. addEventLi stener(ProgressEvent. SOCKET_DATA. socketDataLi stener);

Всякий раз, когда возникает событие ProgressEvent. SOCKET_DATA, вызывается метод экземпляра socketDataListener ( ) класса DisplayAssetLoader. Внутри метода s о eke t Da taLi stener ( ) переменная экземпляра byte s Avail able класса Socket обозначает количество байтов в сокете, доступных для чтения на настоящий момент. Метод socketDataListener ( ) добавляет новые полученные данные в объект ByteArray, на который ссылается переменная buffer. Чтобы прочитать новые данные и сохранить их в объекте buffer, мы используем метод экземпляра readBytes ( ) класса Socket, который принимает следующий общий вид:

o6beKTSocket. rea6Bytes(6aiiTbi, смещение, длина)

Здесь объектБоскеЬ — это объект Socket, из которого будут прочитаны байты, байты — объект ByteArray, в который будут записаны байты, смещение — позиция внутри объекта байты, с которой начнется запись, а длина — количество байтов, которое будет прочитано из сокета. В нашем случае мы хотим прочитать все байты из сокета и сохранить их в объекте buffer, начиная с конца объекта buffer. Следовательно, мы используем такой код:

socket. readBytes(buffer, buffer. length, socket. bytesAvai1able);

Рассмотрим полный листинг кода для метода socketDataListener ( ):

private function socketDataListener (e:ProgressEvent):void { out(«New socket data arrived.»);

socket. readBytes(buffer, buffer. length, socket. bytesAvai1able);

}

Создание отображаемого элемента из загруженных байтов

Как только все байты для элемента будут получены, мы можем использовать метод экземпляра loadBytes ( ) класса Loader для создания из этих байтов экземпляра класса DisplayObject языка ActionScript. Напомним, что сервер FileSender сообщает о завершении передачи данных в полном объеме путем простого закрытия сокетного соединения. Поэтому мы помещаем код, создающий наш экземпляр класса DisplayOb j ect («объект элемента»), в приемник события Event. CLOSE. Чтобы зарегистрировать приемник для события Event. CLOSE в объекте socket, мы используем следующий код:

socket. addEventLi stener(Event. CLOSE, closeLi stener);

Вфункции closeListener ( ) перед созданием экземпляра класса DisplayObject мы сначала проверяем, был ли получен байт «завершение передачи» (ASCII-код 4) от сервера. Напомним, что сервер отправляет байт «завершение передачи» в качестве последнего байта потока данных. Чтобы получить этот байт, мы используем следующий код:

buffer. position = buffer. length — 1;

var lastByte:int = buffer. readUnsignedByte( );

Если байт «завершение передачи» отсутствует, бинарные данные элемента были получены не полностью, и поэтому функция-приемник завершает свое выполнение:

if (lastByte!= DisplayAssetLoader. EOT) { return;

Если байт «завершение передачи» был получен, мы создаем объект элемента с помощью метода loadBytes ( ). Тем не менее перед передачей байтов элемента в метод loadBytes ( ) мы должны сначала удалить байт «завершение передачи» из буфера, как показано в следующем коде:

buffer. length = buffer. length — 1:

Теперь, имея на руках байты элемента, мы можем создать объект элемента:

loader = new Loader( ): 1oader.1oadBytes(buffer):

После завершения выполнения предыдущего кода запускается процесс генерации объекта элемента, однако сам объект элемента пока недоступен. Как и в случае с методом экземпляра load ( ) класса Loader, если попытаться обратиться к объекту элемента через переменную loader. content сразу после вызова метода loadBytes ( ), будет возвращено значение null. Таким образом, перед тем как обратиться к объекту элемента, мы должны дождаться возникновения события Event. INIT:

loader. contentLoaderInfo. addEventListener(Event. INIT.

Руководство по actionscript. часть 6, стр. 052

AssetlnitListener);

Когда элемент станет доступен, мы добавляем его на экран:

private function assetlnitListener (e:Event):void { addChi1d( 1 oader. content): outC’Asset initialzed.»):

}

Напомним, что при возникновении события Event. INIT становятся доступными элементы и объекты, созданные в конструкторе основного класса SWF-файла или в коде, который размещается в первом кадре временной шкалы SWF-файла, но при этом элементы и объекты, создаваемые во втором и последующих кадрах,, недоступны. Дополнительные сведения об обращении к визуальным элементам и объектам, создаваемым во втором и последующих кадрах, можно найти ранее в разд. «Обращение к элементам в многокадровых SWF-файлах».

Удаление SWF-элементов, загруженных на этапе выполнения

Чтобы удалить из приложения SWF-элемент, загруженный на этапе выполнения, мы должны сначала обнулить все ссылки на него в приложении, а затем либо обнулить все ссылки на объект Loader, загрузивший этот элемент, либо вызвать метод unload ( ) над данным объектом Loader.

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

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

Приложение, загрузившее элемент, перед удалением этого элемента должно выполнить следующее.

Руководство по actionscript. часть 6, стр. 053

? Если загрузка элемента еще не завершена, приложение должно прекратить операцию загрузки. Например:

try {

theAssetLoader. closet );

}

catch (e:*) {}

? При обнулении всех ссылок на элемент приложение должно удалить элемент из всех родительских экземпляров объекта DisplayObjectContainer. Например:

container. removeChild(theAsset); Сам элемент перед удалением должен выполнить следующие задачи.

? Сообщить любым загруженным дочерним SWF-элементам о необходимости деактивации.

? Остановить воспроизведение любых звуковых файлов.

? Остановить воспроизведение основной временной шкалы, если ее воспроизведение происходит в настоящий момент.

Руководство по actionscript. часть 6, стр. 054

? Остановить воспроизведение любых клипов, проигрываемых в настоящий момент.

? Закрыть любые подключенные сетевые объекты, например экземпляры классов

Loader, URLLoader, Socket, XMLSocket, LocalConnect ion, NetConnections HNetStream.

? Обнулить все ссылки на объекты Camera или Microphone.

? Отменить регистрацию всех приемников событий (особенно это касается события Event. ENTER_FRAME, а также приемников событий мыши и клавиатуры).

? Остановить все интервалы, выполняющиеся в настоящий момент (с помощью метода clearlnterval ( )).

? Остановить все объекты Timer (с помощью метода экземпляра stop ( ) класса Timer).

Элемент должен выполнять перечисленные задачи в собственном методе, освобождающем потребляемые ресурсы, который вызывается приложением перед удалением данного элемента.

За дополнительной информацией о деактивации элементов перед их удалением обратитесь к разд. «Деактивация объектов» гл. 14. Кроме того, прочитайте серию интернет-статей Гранта Скиннера (Grant Skinner) под названием «ActionScript 3.0: Resource Management, опубликованных по адресу http://www. gskinner. com/blog/ archives/2006/06/as3_resource_ma. html.

Мы познакомились со всеми методиками добавления внешнего элемента в приложение на этапе выполнения. Далее рассмотрим, как добавлять внешний отображаемый элемент в приложение на этапе компиляции.



Полезные ссылки
Случайные записи
  • 20.01.2010">Векторные файлы и графика
  • 17.03.2011">Руководство по actionscript. часть 2, стр. 157
  • 22.01.2011">Руководство по actionscript. часть 1, стр. 117
  • 13.06.2010">Самоучитель по креативному веб-дизайну. Книга 4, стр.101
  • 07.11.2011">10 примеров навигации по сайту
  • 18.03.2011">Руководство по actionscript. часть 2, стр. 110
  • 01.04.2012">ЕС создаст центр по борьбе с киберпреступностью
  • 22.01.2011">Руководство по actionscript. часть 1, стр. 114
  • 06.07.2012">Google улучшила службу Search by Image
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.24
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 030
  • 23.01.2011">Руководство по actionscript. часть 1, стр. 034
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.14
  • 13.03.2011">Руководство по actionscript. часть 3, стр. 089
  • 19.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.16
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...