Pull to refresh

Comments 35

Я не против велосипедов, поймите правильно, они порой полезны и, как минимум, развивают мышление.
Но есть же TeamViewer, с клиентской частью для Андроида и iOS.
Вы правы на счет велосипедов, но с другой стороны это хорошая статья, показывающая как сделать простой клиент-сервер передающий скриншоты. Может помочь при изучении платформы WP7
Вообще интересная задумка, но замечания по реализации:

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

var timer = new System.Windows.Forms.Timer() { Interval = 40 };

Не пойдет. Запустите параллельно значительно «отъедающее» мощности приложение (архиватор, перекодирование видео и т.п.) и посмотрите, что станет с таймером. Windows не система реального времени и дискретность срабатывания в 40мс не гарантируется, гарантируется лишь, что таймер сработает не чаще, чем через 40 секунд. А пауза может составить и 50 и 150мс, так что при загруженном процессоре частота обновления будет плавать в зависимости от загрузки.
Обычно в таких ситуациях делают замкнутый цикл с отдачей неиспользованной части кванта процессора, на С++ и WinAPI выглядит примерно так
for ( ;; )
{
/* ToDo */
Sleep( 10 );
}
Ага. А замкнутый цикл, типа, гарантирует. «Обычно» в таких случаях использует Multuimedia Timer, который даёт высокую разрешающую способность (нужно ещё приоритет своей задачи выставить «выше среднего»).
С MMTimer другая засада. Каждое срабатывание этого таймера выполняется в отдельном потоке, поэтому возможны накладки когда предыдущая итерация еще не отработала и началась новая. А значит уже нужна потоковая синхронизация со всеми вытекающими…
Стопать таймер при возникновении события и стартовать после обработки, не?
Не так всё.
ММ таймер может:
1) вызвать пользовательскую функцию в отдельном потоке.
2) Взводить указанный Event.
3) Делать PulseEvent.
И поток там в лучшем случае всего один — поток таймера.
Более того никакая синхронизация не нужна. Делаем приоритет своей задачи выше среднего (чтобы пробуждали без ожидания). Создаём Event и усыпаем в его ожидании. Таймер делает PulseEvent и если мы ждём таймера, то мы тут же (без задержек, т.к. приоритет у нас выше среднего) просыпаемся и делаем своё чёрное дело. Если мы ещё не закончили, то этот самый PulseEvent вообще ничего не делает, т.к. он будет одну ожидающую Event'а задачу, в случае наличия оной.
там в конце вместо «будет» должно быть «будит».
В общем-то, Sleep(10) тоже не гарантирует, что поток проснётся через указанное время, проснуться может и раньше и позже, плюс-минус один квант, что обычно плюс-минус 15-20 мс — так что, как выше сказали, уж лучше мультимедийные таймеры, которые позволяют регулировать эту ошибку.
Читайте внимательно, Sleep( 10 ) не для паузы в 10мс, а именно для организации передачи остатка кванта другим приложениям/нитям.
И как этот способ позволит избавиться от плавающей частоты обновления, вроде об этом разговор шёл? Скажем, одна итерация стабильно отрабатывает за 10 мс — обновление 100 Гц, Sleep поспал не 10 мс, а 15 — 66 Гц и это ещё без полезной нагрузки, которая тоже может за разное время выполняться.

По-моему, надо выбирать такой квант для мультимедийного таймера, чтобы полезная нагрузка успевала отрабатывать, не успевает — выкидываем кадр и увеличиваем квант; всё время успеваем — уменьшаем квант.
Тут другой подход. Стабилизируется не частота, а стабильность.
В цикле, который работает с максимально возможной частотой, замеряем время с прошлой итерации и, если требуется, делаем снимок экрана. Не требуется — можно сделать что-то еще полезное.
Не нравится Sleep(10) можно поставить Sleep(0) или SwitchToThread().
А тут не нужна высокая точность. Тем более на этапе такого простого прототипа.
Более того, если вё происходит точно так, как вы описываете (под нагрузкой интервалы увеличиваются), то это скорее полезное свойство.
То что интервалы увеличиваются — безусловно полезное свойство, но слишком уж WM_TIMER чувствителен, достаточно просто мышкой водить туда-сюда и уже видно, как проседает частота его срабатывания.
Я бы не назвал это велосипедом Это собственный проект автора данного топика. Если он будет его развивать, то на свет, кто знает, может получиться ещё один хороший продукт. Когда зарождался Google, Yahoo уже во всю занимались поиском. Точно также как в момент старта Yandex все уже знали Rambler. Если бы все так рассуждали, то не было бы прогресса.
>> Делаем сами remote-desktop клиент для смартфона
>> Но есть же TeamViewer, с клиентской частью для Андроида и iOS
Хотелось бы сразу видеть «заточку» под абстрактного клиента.
Отличный пример для изучения технологии — жду следующую часть статьи.
Чтобы уменьшить трафик, можно разделять картинку на 9 равных частей, например, и передавать лишь только ту, которая изменилась.
Кстати интересная идея. Можно вообще quadtree или octree применить для оптимизации :)
Чтоб уменьшить трифик можно во 1 ресайзить картинку на сервере под выбранный масштаб на клиенте (если такое поддерживается), во 2-ых использовать формат поддерживающий компрессию и альфа-канал для того чтоб передавать только изменившиеся фрагменты. Я для такого-же приложения использовал PNG с поддержкой адаптивной палитры. Но тут нужно отметить что размер одного «фрейма» может быть достаточно большим чтоб уместиться в один UDP пакет. Также поскольку UDP протокол не является надежным, возможна потеря пакетов нажатия клавиш/мишки, что является просто неприемлемым. Это приводит в мысли, что нужно реализовывать «свой» TCP протокол, и если это сделать, то можно получить такие бонусы как UDP hole punching. Я правда не решился на такой шаг и использовал TCP.
Должен признаться что скорость работы немного уступает TeamViewer — видно компрессия у них все-же лучше…
Также можно копать в сторону mirror driver, хотя вроде он не используется в системах с включенной темой Aero.
Если интересно мое приложение или есть вопросы — спрашивайте.
Приложение здесь
1. На мой взгляд выбрать было .NET с его нерасторопностью для приложения очень критичного к задержкам немного мне кажется неверно (переубедите пожалуйста, а то так и буду считать .NET штукой для рисования интерфейсов)

2. Не очень безопасный «проектор» изображения. Хотелось бы конечно сделать скрытую RDP сессию к клиенту и там уже что-то делать. То есть по сути не открывать рабочий стол и не предоставлять пользователю возможность в любой момент выдернуть шнур у управляемого компьютера и продолжить управлять системой от вашего имени пользователя (ну например рассмотреть удаленный вход Админа). Тут надо перехватывать клавиатуру и мышь и блокировать.

3. Понравилась идея с альфа каналом. Возможно есть смысл передавать только блоки с изменениями. Как выделять блоки — можно отдельной статьей (с разжевыванием ctree и т.п.)

4. TCP/UDP хорошо, но как быть когда у обоих Firewall/серые сети и прочие сетевые проблемы?

P.S. Отличная статья академического характера! Обязательно отмечу *!
1. Нет никаких фундаментальных причин, по которым .NET должен быть медленнее, чем, например, C++. Промежуточный байткод — не повод тормозить; байткод используется и при компиляции C++ — в этом легко убедиться препарировав GCC. Просто в GCC он не выходит за границы компилятора.

.NET тормозит на счётных алгоритнах из-за проверок границ массивов. В C# они отключаются с помощью ключего слова unchecked. В C++ их можно «включить» сделав свои собственные bound-checking arrays.

.NET тормозит на сборке мусора. Мусор можно не производить, переиспользуя созданные объекты.

А ещё, .NET я выбрал потому, что в третьей части статьи я собираюсь просто скопировать часть кода «Client» на WP7 и получить почти готового телефонного клиента. Это должно получится красиво.

2. Это только начало. В проекте около двух сотен строк. Безопасность я пока даже не рассматривал.

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

4. WP7 не поддерживает UDP, только UDP-broadcast. Т.е. по UDP оно будет работать только внутри пределов одной подсети.
Можно сделать TCP, но в первую очередь я буду делать UDP.
упс…
Альфа-канал нужен для того чтоб была возможность передавать только изменившиеся пиксели. Т.е. на сервере должен храниться последний переданный снимок и при обработке следующего снимка мы сравниваем его с прежним и для идентичных пикселей устанавливаем прозрачность в 0 в изображении которое собираемся передать…
упс потому что мне показалось что я запостил не весь текст
на самом деле я нажал предпросмотр
нет :( альфа-канал для этого не пригоден. Вот вам контрпример:

допустим, незагрубленное изображение выглядит так:
255,0,255,0,255,0,255,0,255,0,255,0,255,0,255,0 (всего 16)

ну а мы его загрубили, уменьшив в два раза.
127,127,127,127,127,127,127,127 (тут 8)
а на приёмнике, растянули до
127… ( 16 штук )

а потом передаём разницу, с альфаканалом:
(255,255), (0,255), (255,255),… (всего 16 пар, второе число в паре — альфаканал)

меньшее значение альфаканала не подходит, ибо формула такая:
старое*(1-(alpha/255)) + новое*(alpha/255)

таким образом, оказывается, что альфаканал иногда должен принимать своё максимальное значение. Т.е. либо быть максимальным по всей поверхности, либо в некоторых местах.

если во всех — он не несёт информации, и он нужен
еслитолько в некоторых — значит он будет нести в себе информацию, и её придётся бережно сжимать, и она будет занимать место.

ну и какую пользу может принести альфаканал?
что-то мне сложновато понять о чем Вы говорите. Что значить загрубить и зачем уменьшать кадр а потом увеличивать?
Еще раз зачем нужен альфа-канал — с помощью полностью прозрачных пикселей (их цвет вообще не важен) и какого-либо сжатия — я предлагал PNG сжатие — мы получим возможность передавать только разницу между кадрами, и если она не большая — один фрейм будет совсем небольшим.
Алгоритм такой:
prevImage = getSnapshot();
while (true)
{
newImage = getSnapshot();
difference = getDifference(prevImage, newImage); // этот метод возвращает новый bitmap в котором пиксели, которые совпадают с прежними пикселями будут полностью прозрачными и иметь например черный цвет
frame = pngCompress(difference);
sendFrame(frame);
prevImage = newImage;
}

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

на самом деле, для меня, написание серии статей на харбе — просто способ подтвердить квалификацию перед поиском работы.
Сам недавно проходил собеседование. Хочу обратить ваше внимание на то, что работодатель наверняка даст Вам тестовое задание и при оценке Вашей работы будет учитываться не только её функциональность, но и архитектура. В этом же топике, согласитесь, есть изъяны в функциональности, а архитектура просто отсутствует. Я заметил только несколько приватных статических методов, что никак не отражает ваши знания архитектурных приёмов, паттернов и тд.
Может я не в курсе, а разве приложение, написанное на С++, нельзя декомпилировать?
Декомпилировать во что? В исходный код? Полностью наверное будет сложно. Декомпилировать только нужную часть — легче. Откусить защиту — еще легче. Но для всего этого нужно знание ассемблера/низкоуровнего отладчика/(что там еще).
Для декомпиляции .net сборки нужно всего лишь запустить reflector.
Можно же обфусцировать .NET сборку, или ещё как зашифровать, и никакой рефлектор не поможет. Насколько я знаю, в .NET есть определенные средства для защиты исходного кода. Конечно, при нужных навыках, защиту можно сломать, но это относится не только к .NET, но и к любой другой сборке/библиотеке/приложению. Поэтому утверждение «самый серьезный минус.нет — это возможность декомпилировать приложение» я считаю весьма сомнительным.
Обфускация не поможет от реверс инжениринга алгоритма. А нормальная защита для.нет стоит денег (впрочем как и протекторы для нейтив кода) и часто привносит свои фичи (как например делает результат x86 only, хотя я давно не сталкивался с .net протекторами — может и появились более совершенные).
Утверждение «самый серьезный минус.нет — это возможность декомпилировать приложение» относиться к разработке shareware проложений.
По моему от реверс инжениринга не поможет ничего, не зависимо оттого, какая технология используется. Это просто вопрос времени — чем лучше защита, тем дольше ломают. Поэтому я склонен считать возможность получения исходного кода больше фичей, чем минусом. Как бы там ни было, я вашу позицию понял, Вы мою тоже, можно закрыть обсуждение.
Sign up to leave a comment.

Articles