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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1829269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:12:13  来源:igfitidea点击:

Efficient way to send images via WCF?

c#wcf

提问by InvertedAcceleration

I am learning WCF, LINQ and a few other technologies by writing, from scratch, a custom remote control application like VNC. I am creating it with three main goals in mind:

我学习WCF,LINQ和其他一些技术,通过文字,从头开始,像VNC自定义遥控器的应用。我考虑了以下三个主要目标创建它:

  1. The server will provide 'remote control' on an application level (i.e. seamless windows) instead of full desktop access.
  2. The client can select any number of applications that are running on the server and receive a stream of images of each of them.
  3. A client can connect to more than one server simultaneously.
  1. 服务器将提供关于应用级(即无缝窗口),而不是完整的桌面访问“遥控器”。
  2. 客户可以选择任意数量的在服务器上运行的应用程序,并得到他们每个人的图像流。
  3. 客户端可以同时连接到多台服务器。

Right now I am using WCF to send an array of Bytes that represents the window being sent:

现在,我使用WCF要发送的字节代表该窗口发送的数组:

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

GetBitmap implementation:

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;

It is then sent via WCF (TCPBinding and it will always be over LAN) to the client and reconstructed in a blank windows form with no border like this:

它然后通过WCF发送(TCPBinding,它总是会通过LAN)连接到客户端和重建中的空白窗口,像这样无边框形成:

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

I would like to make this process as efficient as possible in both CPU and memory usage with bandwidth coming in third place. I am aiming to have the client connect to 5+ servers with 10+ applications per server.

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

Is my existing method the best approach (while continuing to use these technologies) and is there anything I can do to improve it?

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

Ideas that I am looking into (but I have no experience with):

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

  • Using an open source graphics library to capture and save the images instead of .Net solution.
  • Saving as PNG or another image type rather than JPG.
  • Send image deltas instead of a full image every time.
  • Try and 'record' the windows and create a compressed video stream instead of picture snapshots (mpeg?).
  • 使用开源图形库来捕获和保存图像,而不是 .Net 解决方案。
  • 保存为 PNG 或其他图像类型而不是 JPG。
  • 每次发送图像增量而不是完整图像。
  • 尝试“记录”窗口并创建压缩视频流而不是图片快照(mpeg?)。

采纳答案by Rubens Farias

You should be aware for this points:

您应该注意以下几点:

  • 传输:TCP/二进制消息编码将是传输图像数据的最快方式
  • 图像捕获:您可以依靠 P/Invoke 来访问您的屏幕数据,因为这会更快且更消耗内存。一些示例:在 C# 中捕获屏幕图像 [P/Invoke]如何使用 .NET [托管]使用 C# 捕获屏幕截图(托管)
  • 您应该在发送之前减少您的图像数据;
    • 明智地选择您的图像格式,因为某些格式具有本机压缩(如 JPG)
    • 一个例子应该是查找图像之间的差异 C#
    • 仅发送差异图像,您可以裁剪它并仅发送非空白区域
  • 尝试检查您的 WCF 消息。这将帮助您了解消息的格式并帮助您确定如何使消息更小。

Just afterpassing through all this steps and being satisfied with your final code, you can download VncSharp source code. It implements the RFB Protocol(Wikipedia entry), "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)."

只是之后通过这一切的步骤,并信纳你的最终代码,你可以下载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)."

回答by DxCK

Check out this: Large Data and Streaming(WCF)

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

回答by Jacob Ewald

Instead of capturing the entire image just send smaller subsections of the image. Meaning: starting in the upper left corner, send a 10x10 pixel image, then 'move' ten pixels and send the next 10px square, and so on. You can then send dozens of small images and then update the painted full image on the client. If you've used RDC to view images on a remote machine you've probably seen it do this sort of screen painting.

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

Using the smaller image sections you can then split up the deltas as well, so if nothing has changed in the current section you can safely skip it, inform the client that you're skipping it, and then move onto the next section.

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

You'll definitely want to use compression for sending the images. However you should check to see if you get smaller file sizes from using compression similar to gZip, or if using an image codec gives you better results. I've never run a comparison, so I can't say for certain one way or another.

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

回答by Mikael Svenson

The fastest way to send data between client/server is to send a byte array, or several byte arrays. That way WCF don't have to do any custom serialization on your data.

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

That said. You should use the new WPF/.Net 3.5 library to compress your images instead of the ones from System.Drawing. The functions in the System.Windows.Media.Imaging namespace are faster than the old ones, and can still be used in winforms.

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

In order to know if compression is the way to go you will have to benchmark your scenario to know how the compression/decompression time compares to transferring all the bytes uncompressed.

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

If you transfer the data over internet, then compression will help for sure. Between components on the same machine or on a LAN, the benefit might not be so obvious.

如果您通过互联网传输数据,那么压缩肯定会有所帮助。在同一台机器上或 LAN 上的组件之间,好处可能不那么明显。

You could also try compressing the image, then chunk the data and send asynchronously with a chunk id which you puzzle together on the client. Tcp connections start slow and increase in bandwidth over time, so starting two or four at the same time should cut the total transfer time (all depending on how much data you are sending). Chunking the compressed images bytes is also easier logic wise compared to doing tiles in the actual images.

您还可以尝试压缩图像,然后将数据分块并使用您在客户端上拼凑的块 ID 异步发送。Tcp 连接开始缓慢并且带宽随着时间的推移而增加,因此同时启动两个或四个应该会减少总传输时间(全部取决于您发送的数据量)。与在实际图像中分块相比,将压缩图像字节分块也更容易逻辑。

Summed up: System.Windows.Media.Imaging should help you both cpu and bandwidth wise compared to your current code. Memory wise I would guess about the same.

总结:与您当前的代码相比,System.Windows.Media.Imaging 应该可以在 CPU 和带宽方面为您提供帮助。记忆方面,我猜也是一样的。

回答by Ron Klein

  1. Your solution looks fine to me, but I suggest (as others did) you use tiles and compress the traffic when possible. In addition, I think you should send the entire image once a while, just to be sure that the client's deltas have a common "base".

  2. Perhaps you can use an existing solution for streaming, such as RTP-H263 for video streaming. It works great, it uses compression, and it's well documented and widely used. You can then skip the WCF part and go directly to the streaming part (either over TCP or over UDP). If your solution should go to production, perhaps the H263 streaming approach would be better in terms of responsiveness and network usage.

  1. 您的解决方案对我来说看起来不错,但我建议(和其他人一样)尽可能使用磁贴并压缩流量。此外,我认为您应该偶尔发送整个图像,以确保客户端的增量具有共同的“基础”。

  2. 也许您可以使用现有的流媒体解决方案,例如用于视频流媒体的 RTP-H263。它工作得很好,它使用压缩,并且它有据可查并被广泛使用。然后,您可以跳过 WCF 部分并直接转到流式处理部分(通过 TCP 或通过 UDP)。如果您的解决方案应该投入生产,也许 H263 流媒体方法在响应能力和网络使用方面会更好。

回答by Yannick Motton

I worked on a similar project a while back. This was my general approach:

不久前,我参与了一个类似的项目。这是我的一般方法:

  • Rasterized the captured bitmap to tiles of 32x32
  • To determine which tiles had changed between frames I used unsafe code to compare them 64-bits at a time
  • On the set of delta tiles I applied one of the PNG filters to improve compressability and had the best results with the Paeth filter
  • Used DeflateStreamto compress the filtered deltas
  • Used BinaryMessageEncodingcustom binding to the service to transmit the data in Binary in stead of the default Base64 encoded version
  • 将捕获的位图光栅化为 32x32 的图块
  • 为了确定哪些图块在帧之间发生了变化,我使用了不安全代码来一次比较它们 64 位
  • 在一组增量图块上,我应用了一个 PNG 过滤器来提高可压缩性,并使用Paeth 过滤器获得了最佳结果
  • 使用DeflateStream压缩过滤后的增量
  • 使用BinaryMessageEncoding自定义绑定到服务以 Binary 格式传输数据,而不是默认的 Base64 编码版本

Some client-side considerations. When dealing with large amounts of data being transferred through a WCF service I found that some parameters of the HttpTransportBindingand the XmlDictionaryRenderQuotaswere set to pretty conservative values. So you will want to increase them.

一些客户端注意事项。在处理通过 WCF 服务传输的大量数据时,我发现HttpTransportBindingXmlDictionaryRenderQuotas 的某些参数设置为非常保守的值。所以你会想要增加它们。

回答by OmarElsherif

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;

I use this code to capture my screen.

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