转:http://www.cnblogs.com/mobwiz/p/3715743.html

最近需要在产品中加入桌面共享的功能,暂时不用实现远程控制;参考了园子里的一些文章,加入了一些自己的修改。

需求:将一台机器的桌面通过网络显示到多个客户端的屏幕上,显示内容可能为PPT,Word文档之类的内容,不含视频。

1)抓屏

参考了网上找到的一段代码如下

static BitmapSource CopyScreen()
{
using (var screenBmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var bmpGraphics = Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(0, 0, 0, 0, screenBmp.Size);
return Imaging.CreateBitmapSourceFromHBitmap(
screenBmp.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
}
}

看起来很简洁,但是运行后,发现居然有内存泄漏,内存持续上涨,从30MB一直上涨到了1G多,还不停止,遂修改如下,杜绝了内存泄漏:

调用API中的DeleteObject

 [System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapSource CopyScreen()
{
var handle = IntPtr.Zero;
BitmapSource source = null;
try
{
var bitmap = new Bitmap((int)SystemParameters.PrimaryScreenWidth,
(int)SystemParameters.PrimaryScreenHeight); var g = Graphics.FromImage(bitmap); g.CopyFromScreen(0, 0, 0, 0, bitmap.Size); handle = bitmap.GetHbitmap(); source = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(handle);
} return source;
}

这样处理后,抓屏不再造成内存泄漏,内存占用稳定不再增长 ;

2)传输

因为共享客户端可能是一对多,所以不能再采用TCP点对点传输,尝试使用UDP组播的方式来传输数据,遇到了不少问题;

UDP组播的方法就不再赘述,使用组播的好处在于服务端的网络带宽占用不会因为客户端的增加而增加。

问题一:UDP报文有最大长度限制,所以无法一次发送一张图片

解决方案:自行切分传输,在接收端重组,定义报文类

class Packet
{
public int SN {get;set;}
public byte[] Data {get;set;}
public byte[] ToBytes() {....}
}

切分报文与重组报文,就不再详述。报文发送完毕之后,发送一个END标志,接收端重组报文,再显示图像。本机测试OK,然后放到客户端时,问题出来了。

问题 二:UDP本身是不可靠传输,所以报文到达的顺序可能会乱,也有报文会丢失

解决方案:每一张图片,给予一个GUID,并加上一个时间戳;接收端采用具备缓冲区的接收方式;工作方式如下:

1)发送端:每一个时间间隔抓取一张图片,生成一个Guid,然后进行报文分割,再将报文发送到组播地址,每个报文包含GUID、报文数量、报文序号、时间戳以及数据;

2)接收端:

a)接收到一个报文后,放入报文池;

b)丢弃超时的报文(检查时间戳,超过某个时间的报文丢弃,例如5秒,这种应用不要求严格的数据完整性,收不到的,就丢弃)

c)检查缓冲区内的所有报文,检查将时间最早的报文,并检查其完整性(若报文应该有三个,是否已经收到三个同时具备该GUID的报文),若该GUID的报文已完整,则触发图片准备好的事件(ImageReady),并删除该图片的所有报文数据;

d)客户端通过ImageReady事件显示图像;

测试结果:本机测试OK,局域网测试又失败了,查看调试信息发现客户端总是只能收到所有报文中的第一个@……*#……@,其他的都丢失了~

问题三:局域网上UDP报文丢失?@#

解决方案:发送报文时,加入一定的时间间隔。这个是试出来的,原因可能是发太快了,网络传输不了,就丢失了;

最终代码结构如下:

报文类:

class Packet
{
public double Time {get;set;}
public int Total {get;set;}
public int SN {get;set;}
public byte[] Data {get;set;}
public Packet() {...}
public byte[] ToBytes() {....}
public static Packet FromBytes(byte[] bytes){...}
}
public ShareServer
{
public ShareServer()
{ }
// 伪代码,非可运行代码
public void Start()
{
1)初始化UdpClient,进行组播
// 2)开一个线程,进入循环
while (_doing)
{
var source = CopyScreen(); if (source == null) continue; var guid = Guid.NewGuid();
// JPEG 编码
var enc = new JpegBitmapEncoder() { QualityLevel = 40 };
var ms = new MemoryStream();
enc.Frames.Add(BitmapFrame.Create(source));
enc.Save(ms); // 压缩MemoryStream
// GZipStream
var data = Compress(ms.ToArray());
// 分割报文
var packages = splitPackagets(data, guid);
// 传输报文
foreach (var packaget in packages)
{
var bytesArray = packaget.ToBytes();
_udpClient.Send(packaget.ToBytes(), bytesArray.Length);
// 加入一个间隔
Thread.Sleep(100);
}
// 1 秒抓一次图
Thread.Sleep(1000);
}
}
}

接收端:

class BufferedScreenClient
{
public event EventHandler<ImageReadyEventArgs> ImageReady;
private List<Packet> _packets;
public BufferedScreenClient() {......}
public void AddPacket(Packet packet)
{
1)添加报文
2)清理超时报文
3)检查最早的一张图片是否已经OK,如果OK触发ImageReady事件
a) 首先查询出这一组的报文
b)合并这一组报文,获取数据
c)解压缩
d)JPEG解码成BitmapSource
e)移除该图片的报文数据
f)通过事件,将图片发送给接收端进行显示 _packagets.Add(packet); CleanPackets(); var source = GetFirstImage();
if (source != null)
{
OnImageReady(source);
}
} private void CleanPackets(){....}
private void MergePackets(IEnuerable<Packet> packets){.....}
private BitmapSource GetFirstImage(){......}
}

经测试,一台服务端+两台客户端均可接收到图片,但是第一张图出来的比较慢,网络发送这一块,还需要进行优化。图片质量与发送间隔,可根据网络状况进行调整;

TODO List

1)优化报文发送与报文接收机制

2)图片分块发送

3)比较两次截图之间的差异,如图片没变化就不发送

4)优化接收端的机制

参考:

1)暮雨冰蓝 的C#局域网共享系列文章 http://www.cnblogs.com/liuxiaobo93/p/3675387.html

2)C# 组播

3)WPF 下 抓屏等

(转)C# 使用UDP组播实现局域网桌面共享的更多相关文章

  1. C# 使用UDP组播实现局域网桌面共享

    最近需要在产品中加入桌面共享的功能,暂时不用实现远程控制:参考了园子里的一些文章,加入了一些自己的修改. 需求:将一台机器的桌面通过网络显示到多个客户端的屏幕上,显示内容可能为PPT,Word文档之类 ...

  2. Android上UDP组播无法接收数据的问题

    最近,想做一个跨平台的局域网的文件传输软件,思路是组播设备信息,TCP连接传输文件.于是进行了一次简单的UDP组播测试,发现Android对于UDP组播接收数据的支持即极为有限. 部分代码如下 pac ...

  3. Android设备一对多录屏直播--(UDP组播连接,Tcp传输)

    原文:https://blog.csdn.net/sunmmer123/article/details/82734245 近期需要学习流媒体知识,做一个Android设备相互投屏Demo,因此找到了这 ...

  4. ffmpeg笔记——UDP组播接收总结

    ffmpeg在avformat_open_input里面已经实现了UDP的协议,所以只需要设置好参数,将url传递进去就可以了. 和打开文件的方式基本一样: 01 AVCodecContext *pV ...

  5. 【网络开发】UDP组播接收端解析

    UDP组播接收端解析 网络中的一台主机如果希望能够接收到来自网络中其它主机发往某一个组播组的数据报,那么这么主机必须先加入该组播组,然后就可以从组地址接收数据包.在广域网中,还涉及到路由器支持组播路由 ...

  6. 多网卡情况下接收udp组播

    多网卡下接收udp组播 往往会接收失败 因为用错了网卡 例如我想要接收2网段 其他电脑出的udp组播  我电脑有有线网和wifi在window下可以这样 route add 230.0.0.1 mas ...

  7. 65.QT-UDP组播实现多人共享桌面(同时支持收发显示)

    这里我们只是简单学习下通过udp组播如何共享桌面demo.帧率上面比较低,毕竟没有用推流,只是简单的将图片发送到组播地址,而加入组播地址的客户端去取数据显示而已. 主要是为了学习UDP知识而写的,真的 ...

  8. 桌面共享UDP组播实现

    组播(Multicast)传输:在发送者和每一接收者之间实现点对多点网络连接.如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包.它提高了数据传送效率.减少了骨干网络出现拥塞的 ...

  9. 调皮的udp组播技术

    2017年本科毕业,经历过千辛万苦的找工作之后,我进入了现在的这家公司.虽是职场小白,但励志成为IT界的一股清流(毕竟开发的妹子少,哈哈).因为公司的业务需要,我负责的部分是利用组播技术实现OSG模型 ...

随机推荐

  1. cmake 支持-lpthread

    set(CMAKE_BUILD_TYPE "Release") if( CMAKE_BUILD_TYPE STREQUAL "Debug" )    set(C ...

  2. Effective C++之条款1:视C++为一个语言联邦

    C++中的sub-languages有如下四种: C Object-Oriented C++:   (classes ,encapsulation(封装),inheritance(继承),polymo ...

  3. Replication Controller、Replica Set

    假如我们现在有一个Pod正在提供线上的服务,我们来想想一下我们可能会遇到的一些场景: 某次运营活动非常成功,网站访问量突然暴增 运行当前Pod的节点发生故障了,Pod不能正常提供服务了 第一种情况,可 ...

  4. Change myself to be better

    发现和改变自己不好的习惯 遇到问题的反应 自己遇到问题的时候,特别是不熟悉的问题的时候就会有点焦虑,这个应该是每个人都会有的,遇到自己不熟悉的或者是没有经历过的东西都会 感觉不舒服,或者是遇到难题或者 ...

  5. eclipse背景设置什么颜色缓解眼睛疲劳

    eclipse背景设置什么颜色缓解眼睛疲劳 1.打开window->Preference,弹出Preference面板 2.展开General标签,选中Editors选项,展开. 3.选中 Te ...

  6. NX二次开发-UFUN关闭STL文件函数UF_STD_close_stl_file

    NX9+VS2012 #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include <u ...

  7. NX二次开发-将对象移动到图层UF_OBJ_set_layer

    #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include <uf_layer.h&g ...

  8. 快速排序--Python实现

    快速排序算法:1.选择一个基准数2.小于基准数的放左边,大于基准数的放右边3.利用递归的方法针对左边的数据进行快速排序,再对右边的数据进行快速排序4.递归停止的条件:数组为空或者只有一个元素 时间复杂 ...

  9. (转)Python之路,Day6 - 面向对象学习

    本节内容:   面向对象编程介绍 为什么要用面向对象进行开发? 面向对象的特性:封装.继承.多态 类.方法.     引子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战> ...

  10. 搞懂这7个Maven问题,带你吊打面试官!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:张丰哲 www.jianshu.com/p/20b39ab6a88c 在如今的互联网项目开发当中,特别是Java领域, ...