Pull to refresh

JavaScript, AJAX, Socket и Flash/ActionScript — исследуем вопрос передачи данных в AJAX-приложениях

Reading time 8 min
Views 5.4K
socket_1667_128Как то в последнее время я начал активно повышать свои навыки и знакомиться не только и не столько с новыми технологиями. Например, я уже более-менее освоил Java, а именно — занимаюсь сетевыми сервисами. Также начал работать с ActionScript 3, хотя мое мнение относительно применимости в AJAX-приложениях Flash-компонентов не изменилось — их надо использовать там, где они дают максимальное преимущество, а вот вся «обвязка», например, интерфейс пользователя, можно сделать при помощи стандартных технологий. При разработке AJAX-приложений у разработчика есть достаточно большой выбор для решения задачи обмена данными с сервером. В основном, общение с сервером заключается в...
  • получении первоначальных данных для формирования страницы, это обычно выполняет веб-сервер, так как большинство приложений — это обычные веб-страницы. Для этого может подключаться и сервер приложений, например, PHP или любой другой, то есть, страница может быть как статичной, так и динамической.
  • получение данных для работы — например, когда приложение уже загрузило себя и все необходимое, включая графику, стили и код, идёт запрос данных, допустим — списка писем для отображения в почтовом клиенте. Для приложения на ExtJS (впрочем, любые фреймворки подойдут) обычно данные загружаются в виде JSON или XML, реже используется прямая загрузка кода (то есть, в ответ на запрос сервер формирует HTML-код c включенным в него JavaScript кодом и, возможно, дополнительно, JSON-данные). Кстати, именно этот подход во многих случаях использую и я, он очень удобен — например, если у вас интерфейс с вкладками (табами), то вы можете формировать код для каждого таба в момент первого обращения, а также обновлять, если надо, при каждом посещении. Например, на этом было основан мой последний игровой проект, где так формировались все внутренние информационные блоки.
  • Периодический обмен данными (двусторонний) между приложением и сервером. Например, это команды от пользователя и результат обработки сервером. Не всегда это требует перегрузки интерфейса, я сюда выделяю именно поток данных и команд в границах одного экрана, при этом его состояние не изменяется. Здесь можно применить или прямую загрузку кода в скрытый элемент. Как раз так я и сделал в том игровом проекте — периодически посылался запрос на сервер, а для него был выделен специальный DOM-элемент, который обновлялся при помощи встроенного механизма UpdateManager из ExtJS (подробнее мы уже описывали его архитектуру и методы работы на примере ExtJS 1.1, и материал все ещё актуален, изменения в компоненте минимальны при переходе на другие версии).

Кроме этой достаточно простой классификации я бы хотел поднять еще такой вопрос. По сути, для коммуникации с сервером у нас есть, на самом нижнем уровне, доступном с JavaScript-а, объект XMLHTTPRequest, который приспособлен для передачи только строковых данных по HTTP-протоколу, а также множественные обвязки, врапперы и надстройки, скрывающие детали реализации, и предоставляющие специфический интерфейс для конкретных нужд. Например, в том же ExtJS, на этой основе построены все варианты взаимодействия — и Ext.Update, и Ext.data.Proxy, хотя в последнем случае не все так просто. Осознавая давно уже ограниченность возможностей XMLHTTPRequest (думаю, если у вас есть опыт разработки, то вы знаете о каких ограничениях идет речь), разработчики предусмотрели и другие варианты взаимодействия с сервером, включая экзотические варианты через фреймы (для реализации техники long poll). Для преодоления кросс-доменных ограничений есть и ScriptTagProxy и другие. Но основываясь только на возможностях браузера, HTML-а и JavaScript все же очень сложно реализовать достаточно хорошую, стабильную, а главное, производительную и универсальную систему коммуникации.

Данный вопрос ожидает своего решения кардинальным образом в спецификации HTML5, где будут предусмотрены WebSocket, то есть возможность получить в свое распоряжение и управление через обычный JavaScript возможностей сокет-соединений с сервером. Но вот когда это еще будет…

А такая возможность надо уже сейчас! Да, именно возможность заменить доступ через XMLHTTPRequest (вернее не заменить, а дополнить) на свой протокол, используя стандартные для серверных систем API сокетов, передавая через них как строковые данные, так и традиционные текстовые.

Основное отличие состоит в непрерывности канала передачи и его двунаправленности. То есть, если при традиционном способе мы, по сути, каждый раз инициализируем новый запрос, тратя время на установление соединения (а для HTTP это достаточно накладно, есть нюансы с свойством Keep-Alive, но они мало влияют на общую картину). Да и число параллельных запросов у каждого браузера ограничена. А нам во многих приложениях надо именно после старта установить канал связи и постоянно его держать открытым. Вот в этом нам может помощь модуль реализации сетевых сокетов в JavaScript.

На сегодняшний день есть несколько вариантов реализации такого модуля, два самых распространенных:
  • через JavaFX приложение или Java Applet, который будет взаимодействовать с вашим JavaScript модулем через интерфейс (а он есть у каждой технологии, которая предназначена для внедрения в веб-страницы).
  • через Flash, коммуникация посредством Extertnal Interface, путем внедрения невидимого Flash-объекта.

Хотя по многим параметрам технологически путь взаимодействия через Java/JavaFX кажется более мощным, там есть еще множество сложностей, хотя возможностей там намного больше, включая перенос частично логики сетевого приложения в апплет на сторону клиента. Однако есть и ряд сложностей, например, долгое время инициализации, но это вроде побеждёно в новой архитектуре Java Applet-а, а также сложности с безопасностью и кроссдоменными политиками (но это есть везде, в любой технологии, так как такой модуль потенциальная дыра в безопасности). Кстати, в процесса сбора информации наткнулся на описание того, что вариант с Java не требует policy-файла, но зато jar-файл с приложением должен иметь цифровую подпись, а кроме этого, скрыто инициализировать соединение все же не выйдет — требуется подтверждение от пользователя. Обе технологии позволяют реализовать как бинарные сокеты, для передачи двоичных данных, так и текстовые, но следует учесть, что во Flash это появилось в последних версиях, поэтому ниже 9 (вообще-то 8, но я не уверен) может не работать.

В общих чертах задача следующая — необходим компонент для реализации постоянного соединения с сервером, при этом желательно отвязаться от HTTP-протокола, используя прямое соединение TCP/IP или UDP, а также иметь возможность подключаться к любому хосту и порту. Кроме этого, надо иметь возможность реализовать собственный протокол, передавать любые данные, как в текстовом виде, так и, возможно, двоичные данные, а соединение должно быть постоянным, то есть, после инициализации сокет остается открытым до принудительного закрытия программой или сервером, либо до перезагрузки страницы. Ну и обычные требования — минимальная ресурсоемкость установления и поддержания соединения, кроссплатформеность и кроссбраузерность, объектный интерфейс и возможность управлять и перехватывать все значимые события.

Полностью этим условиям, на мой взгляд, удовлетворяет как раз связка Flash — небольшого модуля на ActionScript, который реализует сокетное соединение, и враппера на JavaScript, который управляет соединением и принимает/отправляет данные. В 9 и выше Flash-е, у нас кроме XMLSocket появился и более низкоуровневый механизм ввода-вывода, который и будем использовать, принимая во внимание его большую универсальность, а также повсеместное распространение 9-ой версии Flash Player-а (а теперь и 10-й).

Одно ещё надо заметить — для коммуникации с удаленным хостом, особенно если это не тот же хост, с которого загружена флешка, а также обязательно, в случае использования портов выше 1024 (здесь я могу ошибаться, поправьте, если кто в теме), необходимо наличие на этом хосте файла crossdomain.xml, в котором описываться политика доступа (хорошая статья по теме). Заметьте, не на хосте, с которого мы соединяемся, а к которому подключаемся! Файл политики должен быть доступен по 80-му порту, либо по другому, 843. Здесь могут возникнуть определенные сложности, если у вас работает только собственный сокет-сервер, который обслуживает другой порт, тогда ради этого надо или поднимать HTTP-сервер дополнительно, или переделать свой, добавив прослушивание этих портов.

Стоит ли писать такой компонент самостоятельно? В принципе, если требуемая функциональность специфична и вы хорошо знаете Flash/ActionScript, то создание такого модуля займет от силы несколько часов, и еще столько-же для написания и отладки JavaScript-враппера для взаимодействия. Но стоит поискать, возможно есть готовые решения? А они есть.
  • Cometdesktop — реализация целого веб-десктопа, на базе как раз ExtJS, с применением реализации веб-сокетов (там даже есть сразу компоненты для ExtJS), впрочем сетевая подсистема там достаточно интересна для изучения, но все же это комплексный продукт, к нашим задачам он мало подходит.
  • Cumhurchat — скорее заявка на реализацию чата на основе сетевого флеш-модуля, с сервером на РНР, но пока дальше анонса дело у разработчиков не зашло. А жаль.
  • Хорошее исследование различных вариантов для технологии GWT, в частности, рассматривается и серверная сторона. Полезно прочитать для именно теоретической части, узнать о совместимости и сложностях в реализации.
  • jssockets — вот это именно то, что нам надо. Очень простой проект, API состоит из нескольких функций и событий (ведь взаимодействие асинхронное), позволяет установить соединение, записать и прочитать строковые данные, а также управлять соединением. Предлагается минимум документации, а код надо брать из SVN, флеш-модуль компилировать вручную. Так что хочется чего-то более развитого...
  • jSocket — лучший на сегодняшний день компонент для реализации сетевой коммуникации на базе Flash socket! Не побоюсь назвать его лучшим, так как предоставляет полный интерфейс сокетов из Flash в JavaScript — любые функции, для записи/чтения любых типов данных (строки, байты, целые числа, числа с плавающей точкой, даже объекты), оповещает обо всех событиях, позволяет встроить модуль в любое приложение. Особенностью библиотеки является поддержка множества независимых соединений — мы можем неограниченно создавать новые сокеты, библиотека держит служебный массив и каждый сокет уникальный (идентифицируется по id, хотя все реализовано через один и тот же SWF-файл). Используется jQuery, однако если посмотреть в исходный код, то библиотечно-зависимого кода там всего несколько строк, поэтому его можно, при надобности, переписать под другой фреймворк.

Есть правда в jSocket один нюанс — библиотека все еще в стадии альфа-версии, хоть и полностью рабочая, запланирована уже и 1.0 релизная версия. Лучше всего брать не доступную собранную версию, а зайти в SVN-репозиторий и получить исходники оттуда. К сожалению, автор занял позицию, что раз альфа, значит никаких гарантий и помощи, кроме доступного описания API и одного примера (я стучал ему в GTalk, но такой был ответ). Кстати, если вам надо, в SVN найдете и простейший сетевой сервер на Java, который может работать в паре с jSocket. Я, к примеру, реализовал собственный на основе фреймворка xSocket (Java NIO фреймворк), а в клиентской части использовал ExtJS вместе с jQuery адаптером, в такой конфигурации библиотека работала без проблем. API достаточно простой, всего одна страница описания функций, а пример охватывает типичное применение библиотеки, поэтому для знакомства вам этого материала будет достаточно, а я в будущем постараюсь написать свое руководство, осталось кое-что переделать на серверной стороне, чтобы можно было создавать свои протоколы обмена на основе строк.

А итог этого исследования прост. Для серьезных AJAX-приложений, если у вас есть несколько точек в архитектуре клиента, которые запрашивают данные и они требуют постоянного обновление, либо периодичность их относительно небольшая, вариант с несколькими соединениями через XMLHTTPRequest (или надстройки, доступные в сторонних фреймворках) может стать узким местом.

Вот для примера: в онлайн игре, что мы делали, у нас было, в лучшем случае 3 запроса на обновления данных, которые работали периодически — для того, чтобы избежать проблем с производительностью пришлось пожертвовать скоростью обновления, буквально вручную расставляя временные интервалы обновления (как сейчас помню, для чата там было 3 секунды на обновление списка сообщений или по факту отсылки своего, для списка онлайн было 7 секунд, периодический heartbeat-запрос, обновляющий статус игрока и получающий еще некоторые данные, работал каждые 15 секунд). И все равно такая схема, особенно если на нее накладываются действия пользователя, которые также генерируют запросы, начинает работать не лучшим образом.

Потому стоит серьезно рассматривать возможность использования сокетного соединения и специального сервера, если вам требуется быстрая реакция и обработка данных.

Касательно сервера — пока лучшим вариантом будет сервер на Java, так как РНР за счет своей сессионной природы работы сведет практически на нет все выгоды, а Java-сервер, правильно спроектированный, вполне сможет обслуживать тысячи одновременных подключений. Если вам этот путь интересен, посмотрите на ещё один проект, найденный в недрах Google CodeGFS Server — open source socket server for Flash and Flex clients. Мы, в рамках своего проекта по созданию открытой игровой платформы, скоро выложим реализацию собственного сервера (Java, на базе фреймворка JBoss Netty), а также упрощенную версию клиентского скрипта на базе jSocket, но переписанного для использования ExtJS, без необходимости сторонних библиотек, ну и поделимся одним из вариантов протокола поверх этой связки.
Tags:
Hubs:
+42
Comments 44
Comments Comments 44

Articles