Расширение проектора: Продолжение 2. LocalConnection UP!
Итак, с инжектом разобрались. Теперь займемся LocalConnection.
В проекторе нам понадобится экземпляр класса ЛЦ. Инициалицировать коннект мы будем примерно таким образом:
Код:
var dll_url = "E" + Math.random(); var prj_url = "P" + Math.random(); dll_url = dll_url.split(".").join(""); prj_url = prj_url.split(".").join(""); var client = new LocalConnection(); client.connect(prj_url); client.send(dll_url, prj_url);
длл и проектор соответственно. Они формируются как строка из случайного набора цифр, дабы обеспечить уникальность оных.
Теперь не страшно, если юзер изволит запустить несколько копий нашего приложения одновременно, надеюсь, понятно почему?
Однако, самое "ноу-хау" кроется в последней строке.
Смотрим: происходит сэнд, но на момент его осуществления НЕТ реального коннекта с именем dll_url!
Поэтому флэш сэндит сообщение, записав в поля заголовка сообщения "размер" и "таймстамп" нули.
Тело сообщения, адресат и пр. AMF-аттрибутика остается как есть. Но, никакой другой нормальный флэшовый ЛЦ данное сообщение не примет.
А вот длл примет, и если это первое сообщение, а оно обязательно должно быть первым, запомнит, какой урл ей дальше слушать (адресат - dll_url) и на какой урл отправлять свои мессаги (метод - prj_url). В этом контексте остается незадействованной возможность передачи некоторых аргументов при сэнде
(третий параметр отсутствует). Аргумент можно заюзать, на пример, для передачи каких-то инит-параметров, дебуг-левел, скажем. Пока оставляю это в стороне, есть интересная мысль как его использовать. Обязательно к этому вернусь.
Все дальнейшие сообщения из проектора так же будут с "нулевыми" размером и таймстампом, другие сообщения длл просто не будет читать, она настроена именно таким образом.
А те, которые шлет длл в проектор будут совершенно нормальные, с размером и таймстампом.
Все это приводит к "невмешательству" нашего ЛЦ проектор-длл в любые другие ЛЦ-каналы, так же как и другие ЛЦ не смогут помешать работе нашего. То бишь, имеем изолированный канал связи, что не может не радовать.
Далее, в ЛЦ (флэшовом) можно передавать любые обЪекты, поддающиеся AMF-сериализации, как то строки, числа, массивы, объекты и т.д. Полный перечень есть в спецификации. Вобщем-то, неплохо. Однако, в длл, по большому счету, нам достаточно реализовать сериализацию/десериализацию данных типа String. Собственно, стринг передается более компактно, чем, скажем, массив. А место нам придется экономить: лимит на объем сообщения 40кБ никто не отменял. Потом, мне показалось проще реализовать некий универсальный механизм парсинга строк в длл с преобразованием кусков в нужные типы данных. По крайней мере, разбора строк для целей оболочки хватает. Я не отметаю напрочь возможности реализации работы и с другими AMF-упакованными данными. Больше всего мне интересно реализовать работу с byte array. Но это уже относится к АС3, и это дело будущего, сейчас речь идет о АС2-версии оболочки..
Получив первое сообщение с именами коннектов, длл пытается вызвать метод init() на стороне проектора. В АС это выглядело бы так:
Код:
lc.send(prj_url,"init",params);
Код:
client.init = function(msg:String) { //здесь разбираем строку msg и отдаем первые команды в оболочку. }
Как организована связь.
Проектор шлет команду, типа "установи позицию окна в (0,0)". Длл, приняв команду, генерит свой внутренний евент, спускающий отдельный поток, в котором команда обрабатывается, сендится результат ее выполнения в проектор и генерится евент "слушай дальше". И так по кругу.
В проекторе команды тоже шлются не абы как. Организуется очередь команд. Зачем? Дело в том, что приниматься за обработку следующей команды, не закончив обработку предыдущей, негоже. Потому каждая команда сендится из проектора в длл только по факту прибытия сообщения с результатом (плохим или хорошим, не важно) предыдущей команды. Ответом длл на первое сообщение будет вызов метода init на стороне клиента в проекторе. Это важно. Именно с него начинается прокачка основной очереди команд.
Проектор стартует со скрытым окном. В обработчик init можно ( и нужно) вставить вызов методов по настройке внешнего вида окна, завершив вызовом метода show(), дабы окно показать. Или как минимум просто вызов show(). Это не единственный путь "инициализации" приложения, есть мысль отправлять некий сценарий инициализации прямо в превом запросе (третий параметр первого send'а в длл, помните?), тогда init() останется просто как дополнительная опция.
Далее я приведу примеры, а сейчас один момент.
Из сказанного видно, что наша оболочка обладает одной весьма важной и не очень приятной особенностью: команды, отсылаемые оболочке выполняются асинхронно с кодом, их пославшим, т.е. с АС-кодом. В целом, все это сильно походит на взаимодействие "клиент-сервер", когда отослав запрос, вы вынуждены ожидать ответ, чтобы продолжить работу. Т.е. в АС нельзя написать что-то типа
Код:
var result = any_method(any_params);
Код:
any_method(any_params);// это мы просто отправили команду в длл. listener.onData = function(message){ // здесь обрабатываем результат выполнения any_method() }
Ставить обработчик на результат каждого метода не обязательно. Прокачка очереди команд организована независимо от того, есть обработчик на результат предыдущей команды или нет.
Вобщем, все сказанное выше, я попытался облачить в набор АС2-классов. Причем классов, в которых есть только статические члены. Это позаимствовано у цинка, где "оболочечные" классы представляют из себя просто наборы статических функций.
Далее, я приведу перчень классов, которые уже реализованы в той или иной степени в длл-оболочке с их кратким описанием. Общее их количество 15 (пока). Число методов в совокупности около 100. Ну, и примеры, конечно.
Всего комментариев 31
Комментарии
![]() ![]() |
|
Конечно, можно остановить выполнение АС. метод delay(miliSec:Number) - один из первых, что я реализовал, помнится)). Но тут есть специфика, связанная с ЛЦ. Насколько я понял, канал ЛЦ плеер "опрашивает" самопроизвольно, ну т.е. просто в основном потоке наряду с остальными функциями стоит, видимо и этот "опрос". Скажем, если приостановить обработку оконной функции все замрет. Но не факт, что замрет именно прерд вызовом опроса, может после пробуждения таки выполнится какой-то другой кусок кода или еще что.., вобщем, пока это дело темное. Принудительно заставить плеер опросить канал и забрать данные не получается. Да и не в этом дело даже. Сам ЛЦ так реализован, что нету у него точки возврата. send() и все. Поток пошел дальше. И пофик, куда там send() ушел. Увы, синхронность тут в пролете.
В цинке все иначе. Там юзается ExternalInterface, и там все по-другому. Там реально получаем синхронный вызов. call() "приостанавливает" поток, точнее, все выглядит так, как если бы плеер выполнял обычный АС код, ну типа подвызова функции, только подвызов уходит в оболочку. |
|
Обновил(-а) alexcon314 18.06.2009 в 10:39
|
![]() ![]() |
|
Цитата:
Конечно, можно остановить выполнение АС. метод delay(miliSec:Number) - один из первых, что я реализовал, помнится)).
|
![]() ![]() |
|
Это я про свою "оболочку".
|
![]() ![]() |
|
Цитата:
Если интересно будет, могу подкинуть по этой теме много идей.
|
![]() ![]() |
|
Интересно. До этого не добрался сам.
Ну, так какие идеи? Я имею в виду синхронизацию без ExternalInterface? Да и еще, по каким законам происходит сериализация в SharedObject? ну и десереализация? ЛЦ - это что-то вроде шаредов, только "онлайн" и можно сразу просекать версию клиента. А в шаредах тогда как? |
|
Обновил(-а) alexcon314 29.07.2009 в 14:22
|
![]() ![]() |
|
![]() ![]() |
|
В шаредах в отличие от LC разрабы оставили пропертя defaultObjectEncoding и objectEncoding, так что там все вручную рулится.
|
![]() ![]() |
|
Т.е. вешать плеер? Ну, идея не так уж безумна.. Только вряд ли кто-нибудь установит флаг - онстатус обрабатывается в одном потоке с циклом. Если в цикл засадить проверку чего-нибудь "внешнего", того же шареда, к примеру - куда ни шло. Это получится типа эмуляция двух потоков (плеера и оболочки) с одним объектом синхронизации (шаредом) в их общем пользовании. Жаль только Sleep или Wait во флэше нету.
defaultObjectEncoding скорее превнесли а не "оставили", т.е. сделали новое свойство. Раньше такого не было. А разрулить можно только в девятке. Но это в смысле применения в оболочке не особо критично, думаю. |
|
Обновил(-а) alexcon314 29.07.2009 в 14:38
|
![]() ![]() |
|
Эммм... не понял. Что значит "ждем возврата от ExternalInterface.call()"?
ExternalInterface.call() сама по-себе синхронаая операция. Или я не прав? Чего там "ждать" и "вешать" - все само "вешается" как надо. Про две возможности - это да, я тоже думаю, что это может быть полезно. Либо синхронный вызов, либо, если нужда какая - асинхронный: отправил в обработку отделный поток, тот отработал и отдуплился. Прикольно. Практической ценности пока ноль, но в этом что-то есть. Вобщем-то, ExternalInterface - "это просто". Но использование ActivX-плеера легально только если юзер сам его установил в систему, предварительно скачав с сайта адоба. А если не установит все накрывается медным тазом. А вам не приходилось делать API-перехват? |
|
Обновил(-а) alexcon314 29.07.2009 в 14:50
|
![]() ![]() |
|
Ну вот, я и говорю "Да зравствует стандартный проектор!"
![]() ![]() ![]() Конечно, можно в свое удовольствие юзать ActiveX, только как-то это скучно. Можно фреймворк какой нить смастрячить типа f-in-box'a, только кому он нужен? Я даже сомневаюсь что и расширение стандартного проектора, даже если что-то и выйдет, будет иметь успех... ибо, есть тот-же цинк. |
|
Обновил(-а) alexcon314 29.07.2009 в 15:13
|
![]() ![]() |
|
Ааа.. вот это не очень понятно. По смутным обрывочным сведениям у мдм есть древний договор с макромедией еще, который якобы не утратил юридической силы до сих пор. Кроме того, они ссылаются вот сюда. Другие, типа финбокса, вообще плеер не распространяют, либо тоже клянутся в наличии какой-то идульгенции.
Вот тут было обсуждение. |
|
Обновил(-а) alexcon314 29.07.2009 в 15:25
|
![]() ![]() |
|
Да никак. Мы не распространяем, и точка. Все под твою юзерскую ответственность.
|
![]() ![]() |
|
Ничто не мешает держать мютекс, это да. Но, то что "...пока не вычитает нули..." - это вряд ли. send() возвращает управление, если была произведена успешная запись в маппинг-файл и мютекс им отпущен. Отправителю пофик, кто и когда проичитает мессадж. Нули - это признак, что адресат получил мессадж, и ждать пока он получит, не отпуская мютекс при этом, можно вечно.
Потом, получил я мессадж, захватил мутекс, держу пока не отработаю, отработал-отпустил. Но синхронизировать не выйдет. Пока я получу мютекс и залочу, пока поток плеера дойдет до обращения к захваченному мютексу на следующем витке проверки и встанет в очередь ожидания.. Вобщем, пара - тройка ас-строчек выполнится, а может и еще чего, скажем кадр нагружен или еще там что.. . и возврат произойдет не туда.. ЛЦ - штука отличная, но синхронизировать лц-вызовы нельзя, так по крайне мере мне думается сейчас. А вот апи-перехват - это уже теплее. Думаю, использовать этот вариант, как основной, лц оставить для евентов, ну и если замахиваться, то и на асинхронные вызовы. Для перехвата выбрал обращения к шаредам. Запись в шаред - это WriteFile(), ловим, читаем команду, физически в файл можно не писать. Обрабатываем, отпускаем (return). В ас за записью ставим сразу чтение - это Readfile(), ловим, подставляем результат, отпускаем. В итоге в ас после записи-чтения имеем на выходе результат обработки команды. Насколько мне удалось выяснить, все операции с локальными шаредами в плеере синхронизированы. Но тут есть свои заморочки: плеер может брать шареды не с диска а из памяти, если неправильно к ним обращаться в ас, т.е. ловля ReadFile в пролете; что-то не ладится с буферами при подстановке в ReadFile(), не пойму пока что именно; нужно предусмотреть обращение плеера к локальным настройкам размеров шаредов и подставить в момент этого обращения нужные настройки; version depended: разные плееры по-разному реализуют работу с шаредами и на уровне апи и на уровне сериализации, то биш протокола... Но если выгорит - было бы кулл: синхронность+отсутствие ограничений на размер мессаджа в обе стороны. |
|
Обновил(-а) alexcon314 29.07.2009 в 16:54
|
![]() ![]() |
|
А у шаредов тоже лимит в 40К?
|
![]() ![]() |
|
Нет. Можно выставить анлимит в момент чтения настроек. Но если настроечных фалов нет, плеер генерит их по дефолту, принимает дефолтную 100к настройку на размер и тут поможет только перезапуск, когда он попытается считать настройки со сгенеренного файла.
|
|
Обновил(-а) alexcon314 29.07.2009 в 16:57
|
![]() ![]() |
|
Спасибо
![]() Съезжу в отпуск и займусь вплотную ![]() |
Последние записи от alexcon314
- Пишем свою оболочку для FP под Windows. Шаг 6. (19.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 5. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 4. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 3. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 2. (17.05.2011)