(转)C# 使用UDP组播实现局域网桌面共享
转: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 下 抓屏等
转载于:https://www.cnblogs.com/wangle1001986/p/4095466.html
(转)C# 使用UDP组播实现局域网桌面共享相关推荐
- udp组播的应用场景
参考: <TCP/IP详解 卷一> 单播 unicast 在单播的情况下,任意两个主机的通信不会干扰网内其他主机(可能引起争夺共享信道的情况除外) 广播 有时候一个主机要向网上的所有其他主 ...
- UDP组播开发测试实例
一 前言 (1)负责项目中组播开发测试工作.期间由于缺乏设备的原因,中间搁置了好久.直到昨天下午临近下班,终于,完成udp组播整个的开发测试工作. 二 开发过程 (1)首先从实现udp协议说起.udp ...
- ios 接收 c# socket udp 组播
最近用wcf 服务 给ios和安卓做接口,做了几个ios的项目 用udp 组播 让ios多终端接收和刷新方法 做一个简单的小例子会把工程给大家下载的 c#代码:netSocketUDP.rar io ...
- 基于stm32f107 stm32cube 和 LWIP 协议实现 udp 组播通信
最近在做一个基于stm32f107 实现 UDP 组播通信的项目,项目基于 stm32cube 配置生成,如下图: UDP组播头文件: #ifndef __MULTICAST_H__ #define ...
- UDP 组播---你需要了解这些
先来了解下UDP UDP 是UserDatagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一 ...
- UDP 组播---基本概念
本文章已收录于: 先来了解下UDP UDP 是UserDatagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式 ...
- 【网络工具】使用 iperf测试 udp组播
文章目录 下载iperf程序 测试组播协议 pc1 客户端发送组播包 pc2 服务端接收组播包 参数说明 -B, --bind host 服务端专用参数 -T, --ttl 客户端专用参数 下载ipe ...
- Qt网络编程-简易版UDP组播通信入门Demo(5)
Qt网络编程-简易版UDP组播通信入门Demo(5)
- UDP组播调试(使用TCPUDP调试工具)
如何使用调试工具进行UDP组播调试 一.下载UDP&TCP调试工具 二.双击打开调试工具并建立端口 2.1 创建组播客户端1 类型:UDP(组播模式):目标IP:225.0.0.20:端口:6 ...
最新文章
- Single Shot Multibox Detection (SSD)实战(上)
- sql server 2008学习3 表组织和索引组织
- CGContextAddLines和CGContextAddLineToPoint在线条半透明时候的区别
- 十分钟学会制作一个漂亮的在线文档
- jQuery Howto: 如何快速创建一个AJAX的加载的图片效果
- 【poj1995】Raising Modulo Numbers
- 600个超实用ICON图标矢量
- 英语写作神器Quillbot---如何使用免费的Premium功能
- 【FTP】FTP连接时出现“227 Entering Passive Mode”的解决方法
- Linux文件系统(一)——常用文件系统
- 打开窗,让阳光洒进来
- java实现抛物线轨迹计算_JavaFX中抛物线轨迹的时间线
- 太酷了!用200个LED做一个智能手表(开源)
- webrtc服务器开发--搭建环境
- 如何安装cygwin
- 栖息谷访谈-与佛结缘,访钟声悠扬DEEPLIN
- 爬取搜搜问问的一个实例
- 一年级课程表(3月28日-4月1日)
- Mac_ThinkPHP报错:SQLSTATE[HY000] [1049] Unknown database 'XXX'
- 超跌反弹之选牛股技巧
热门文章
- springboot + vue 后台token生成 拦截器 redis实现 前台封装axios xueX 接口实现
- What's ASM?
- 野火串口调试助手Linux,野火串口调试助手 支持在线安装方式,永远保持最新版本
持常用的110-1152 联合开发网 - pudn.com...
- 告诉你关于卡巴斯基的三十八个缺点
- vb远程访问dde服务器,VB 利用DDE进程间通信,5行代码搞定
- Windows 修改本地的IP ,改成自己喜欢的IP
- 物理万有引力与航天总结
- 电脑中毒软件都变成java图标,电脑桌面图标变成黑色方块该怎么解决?
- Unity手游制作记-制作通用输入管理器
- 【笔记】制作自己的MSCOCO数据集(VOC2COCO)