Подтвердить что ты не робот

Эффективный способ отправки изображений через WCF?

Я изучаю WCF, LINQ и несколько других технологий, пишу с нуля, обычное приложение для дистанционного управления, такое как VNC. Я создаю его с учетом трех основных целей:

  • Сервер будет предоставлять "дистанционное управление" на уровне приложений (то есть без окон) вместо полного доступа к рабочему столу.
  • Клиент может выбрать любое количество приложений, работающих на сервере, и получать поток изображений каждого из них.
  • Клиент может подключаться к нескольким серверам одновременно.

Сейчас я использую WCF для отправки массива байтов, который представляет отправленное окно:

using (var ms = new MemoryStream()) {
    window.GetBitmap().Save(ms, ImageFormat.Jpeg);
    frame.Snapshot = ms.ToArray();
}

Реализация GetBitmap:

var wRectangle = GetRectangle();
var image = new Bitmap(wRectangle.Width, wRectangle.Height);
var gfx = Graphics.FromImage(image);

gfx.CopyFromScreen(wRectangle.Left, wRectangle.Top, 0, 0, wRectangle.Size, CopyPixelOperation.SourceCopy);

return image;

Затем он отправляется через WCF (TCPBinding и всегда будет по локальной сети) клиенту и реконструируется в пустой форме окна без такой границы:

using (var ms = new MemoryStream(_currentFrame.Snapshot))
{
    BackgroundImage = Image.FromStream(ms);
}

Я хотел бы сделать этот процесс максимально эффективным как в CPU, так и в использовании памяти с пропускной способностью, идущей на третье место. Я хочу, чтобы клиент подключился к 5+ серверам с 10 приложениями на сервер.

Является ли мой существующий метод лучшим подходом (продолжая использовать эти технологии), и есть ли что-нибудь, что я могу сделать для его улучшения?

Идеи, которые я ищу (но у меня нет опыта):

  • Использование графической библиотеки с открытым исходным кодом для захвата и сохранения изображений вместо решения .Net.
  • Сохранение как PNG или другого типа изображения, а не JPG.
  • Каждый раз отправляйте изображение deltas вместо полного изображения.
  • Попробуйте "записать" окна и создать сжатый видеопоток вместо снимков (mpeg?).
4b9b3361

Ответ 1

Вы должны знать об этом:

  • Транспорт: кодирование TCP/двоичного сообщения будет самым быстрым способом передачи ваших данных изображения.
  • Захват изображения: вы можете полагаться на P/Invoke для доступа к данным экрана, так как это может быть быстрее и потреблять больше памяти. Некоторые примеры: Захват изображения экрана в С# [P/Invoke], Как сделать снимок экрана с помощью .NET [Managed] и Захват скриншотов с помощью С# (Managed)
  • Вам следует уменьшить данные изображения перед отправкой;
    • выберите формат изображения с умом, так как некоторые форматы имеют собственное сжатие (как JPG)
    • пример должен быть Найти различия между изображениями С#
    • отправка только diff-изображения, вы можете обрезать его и просто отправить непустые области
  • Попробуйте проверить ваши сообщения WCF. Это поможет вам понять, как форматируются сообщения, и поможет вам определить, как уменьшить количество сообщений.

После прохождения всех этих шагов и удовлетворения вашего окончательного кода вы можете скачать исходный код VncSharp. Он реализует протокол RFB (запись в Википедии), "a simple protocol for remote access to graphical user interfaces. Because it works at the framebuffer level it is applicable to all windowing systems and applications, including X11, Windows and Macintosh. RFB is the protocol used in VNC (Virtual Network Computing)."

Ответ 3

Я работал над подобным проектом некоторое время назад. Это был мой общий подход:

  • Растрирование захваченного растрового изображения до фрагментов 32x32
  • Чтобы определить, какие плитки были изменены между кадрами, я использовал небезопасный код для сравнения их 64-битных за раз
  • На множестве дельта-плиток я применил один из фильтров PNG для улучшения сжимаемости и имел наилучшие результаты с Paeth filter
  • Используется DeflateStream для сжатия фильтрованных дельта
  • Использовать BinaryMessageEncoding настраиваемую привязку к службе для передачи данных в двоичном формате вместо кодированной по умолчанию версии Base64

Некоторые соображения на стороне клиента. При работе с большими объемами данных, передаваемых через службу WCF, я обнаружил, что некоторые параметры HttpTransportBinding и XmlDictionaryRenderQuotas были настроены на довольно консервативные значения. Поэтому вы захотите увеличить их.

Ответ 4

Самый быстрый способ отправки данных между клиентом/сервером - отправить массив байтов или несколько байт-массивов. Таким образом, WCF не должен выполнять пользовательскую сериализацию ваших данных.

Это сказало. Вы должны использовать новую библиотеку WPF/.Net 3.5 для сжатия ваших изображений, а не из System.Drawing. Функции в пространстве имен System.Windows.Media.Imaging быстрее, чем старые, и все еще могут использоваться в winforms.

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

Если вы передаете данные через Интернет, сжатие поможет. Между компонентами на одном компьютере или в локальной сети преимущество может быть не столь очевидным.

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

Подсчитано: System.Windows.Media.Imaging должен помочь вам как cpu, так и пропускной способности по сравнению с вашим текущим кодом. Память мудрая я бы догадался о том же.

Ответ 5

Вместо захвата всего изображения просто отправьте небольшие подразделы изображения. Значение: начиная с верхнего левого угла, отправить изображение размером 10x10 пикселей, затем "переместить" десять пикселей и отправить следующий квадрат 10 пикселей и так далее. Затем вы можете отправить десятки небольших изображений, а затем обновить окрашенное полное изображение на клиенте. Если вы использовали RDC для просмотра изображений на удаленном компьютере, вы, вероятно, видели, что он делает такую ​​картину на экране.

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

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

Ответ 6

  • Ваше решение отлично выглядит для меня, но я предлагаю (как и другие) использовать плитки и сжимать трафик, когда это возможно. Кроме того, я думаю, что вы должны отправить весь образ раз в то время, просто чтобы убедиться, что клиентские дельта имеют общую "базу".

  • Возможно, вы можете использовать существующее решение для потоковой передачи, такое как RTP-H263 для потоковой передачи видео. Он отлично работает, он использует сжатие, и он хорошо документирован и широко используется. Затем вы можете пропустить часть WCF и перейти непосредственно к потоковой части (через TCP или через UDP). Если ваше решение должно идти на производство, возможно, потоковый подход H263 будет лучше с точки зрения оперативности и сетевого использования.

Ответ 7

Bitmap scrImg = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics scr;
scr.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.PrimaryScreen.Bounds.Size);
testPictureBox.Image = (Image)scrImg;

Я использую этот код для захвата экрана.