通过WCF发送图像的有效方式?

我正在学习WCF,LINQ和其他一些技术,从头开始编写自定义远程控制应用程序,如VNC。 我正在创建它,主要考虑三个主要目标:

  1. 服务器将在应用程序级别(即无缝窗口)上提供“远程控制”,而不是完全桌面访问。
  2. 客户端可以选择服务器上运行的任意数量的应用程序,并接收每个应用程序的图像流。
  3. 客户端可以同时连接到多个服务器。

现在我正在使用WCF发送一个表示正在发送的窗口的Bytes数组:

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,它将始终通过LAN)发送到客户端,并在没有边框的空白窗体中重建,如下所示:

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

我想在CPU和内存使用方面尽可能提高这个过程的效率,带宽排在第三位。 我的目标是让客户端连接到5台服务器,每台服务器有10多个应用程序。

我现有的方法是最好的方法(同时继续使用这些技术)并且我能做些什么来改进它?

我正在研究的想法(但我没有经验):

  • 使用开源图形库来捕获和保存图像而不是.Net解决方案。
  • 保存为PNG或其他图像类型而不是JPG。
  • 每次发送图像增量而不是完整图像。
  • 尝试并“记录”窗口并创建压缩video流而不是图片快照(mpeg?)。

你应该知道这一点:

  • 传输:TCP /二进制消息编码将是传输图像数据的最快方式
  • 图像捕获:您可以依靠P / Invoke来访问您的屏幕数据,因为这可以更快,更耗费内存。 一些示例: 在C#[P / Invoke]中捕获屏幕图像 , 如何使用.NET [Managed] 拍摄屏幕截图并使用C#捕获屏幕截图(Managed)
  • 您应该在发送之前减少图像数据;
    • 明智地选择图像格式,因为某些格式具有本机压缩(如JPG)
    • 一个例子应该是找到图像之间的差异C#
    • 只发送差异图像,你可以裁剪它,只发送非空区域
  • 尝试检查您的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)."

看看这个: 大数据和流媒体 (WCF)

我不久前在一个类似的项目上工作过。 这是我的一般方法:

  • 将捕获的位图栅格化为32×32的切片
  • 为了确定哪些区块在帧之间发生了变化,我使用了不安全的代码来一次比较64位
  • 在delta图块集上,我应用了一个PNG滤镜来提高可压缩性,并使用Paeth滤镜获得最佳效果
  • 使用DeflateStream压缩过滤的增量
  • 使用BinaryMessageEncoding自定义绑定到服务以在Binary中传输数据而不是默认的Base64编码版本

一些客户端考虑因素。 当处理通过WCF服务传输的大量数据时,我发现HttpTransportBinding和XmlDictionaryRenderQuotas的一些参数被设置为相当保守的值。 所以你会想要增加它们。

在客户端/服务器之间发送数据的最快方法是发送一个字节数组或几个字节数组。 这样WCF就不必对数据进行任何自定义序列化。

那就是说。 您应该使用新的WPF / .Net 3.5库来压缩您的图像,而不是System.Drawing中的图像。 System.Windows.Media.Imaging命名空间中的函数比旧的更快,并且仍然可以在winforms中使用。

为了了解压缩是否可行,您必须对方案进行基准测试,以了解压缩/解压缩时间与传输所有未压缩字节的比较。

如果您通过互联网传输数据,那么压缩将有助于确保。 在同一台机器或局域网上的组件之间,好处可能不那么明显。

您还可以尝试压缩图像,然后对数据进行分块,并与您在客户端上拼图的块ID异步发送。 随着时间的推移,Tcp连接启动缓慢并且带宽增加,因此同时启动两个或四个应该减少总传输时间(所有这些都取决于您发送的数据量)。 与在实际图像中进行切片相比,对压缩图像字节进行分块也更容易逻辑化。

总结 :与当前代码相比,System.Windows.Media.Imaging应该可以帮助您提高CPU和带宽。 记忆明智,我猜也一样。

而不是捕获整个图像只是发送图像的较小子部分。 含义:从左上角开始,发送一个10×10像素的图像,然后“移动”十个像素并发送下一个10像素的方块,依此类推。 然后,您可以发送数十个小图像,然后在客户端上更新绘制的完整图像。 如果您使用RDC在远程计算机上查看图像,您可能已经看到它会进行这种屏幕绘制。

使用较小的图像部分,您也可以分割增量,所以如果当前部分没有任何变化,您可以安全地跳过它,通知客户端您正在跳过它,然后转到下一部分。

您肯定希望使用压缩来发送图像。 但是,您应该检查是否使用类似于gZip的压缩来获得较小的文件大小,或者如果使用图像编解码器可以获得更好的结果。 我从来没有进行过比较,所以我不能以某种方式说出来。

  1. 您的解决方案对我来说很好,但我建议(与其他人一样)您使用磁贴并尽可能压缩流量。 另外,我认为你应该一次发送整个图像,只是为了确保客户端的增量有一个共同的“基础”。

  2. 也许您可以使用现有的流媒体解决方案,例如用于video流的RTP-H263。 它运行良好,它使用压缩,并且有很好的文档和广泛使用。 然后,您可以跳过WCF部分并直接转到流部分(通过TCP或UDP)。 如果您的解决方案应该投入生产,那么在响应性和网络使用方面,H263流媒体方法可能会更好。

 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; 

我使用此代码捕获我的屏幕。