(转)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 下 抓屏等

时间: 2024-10-25 16:06:33

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

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

最近需要在产品中加入桌面共享的功能,暂时不用实现远程控制:参考了园子里的一些文章,加入了一些自己的修改. 需求:将一台机器的桌面通过网络显示到多个客户端的屏幕上,显示内容可能为PPT,Word文档之类的内容,不含视频. 1)抓屏 参考了网上找到的一段代码如下 static BitmapSource CopyScreen() { using (var screenBmp = new Bitmap((int)SystemParameters.PrimaryScreenWidth, (int)Syst

Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select poll epoll udp组播 线程池

[本文谢绝转载原文来自http://990487026.blog.51cto.com] Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select  poll  epoll udp组播 线程池 TCP 11种状态理解: 1,客户端正常发起关闭请求 2,客户端与服务端同时发起关闭请求 3,FIN_WAIT1直接转变TIME_WAIT 4,客户端接收来自服务器的关闭连接请求 多路IO转接服务器: select模型 poll模型 epoll模型 udp组播模型 线

JAVA实现UDP组播聊天程序

分类: Java 一.实验环境 编程语言:Java1.5(运行在JVM(Java Virsual Machine)) 开发工具:eclipce3.2 测试环境:局域网 二.实验目的 社会已经进入信息时代,网络技术在飞速发展.大量应用都依赖于从一个主机向多个主机或者从多个主机向多个主机发送同一信息的能力,在Internet上分布的数目可能达数十万台,这些都需要更高的带宽,并且大大超出了单播的能力.一种能最大限度地利用现有带宽的重要技术是IP组播. 三.组播聊天程序的实现: /** * 该程序实现了

Android对UDP组播接收数据的有限支持(一)

最近,想做一个跨平台的局域网的文件传输软件,思路是组播设备信息,TCP连接传输文件.于是进行了一次简单的UDP组播测试,发现Android对于UDP组播接收数据的支持即极为有限. 部分代码如下 1 package com.hsocket.Udp; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.DatagramSocket; 6 7 public class UdpReceiv

UDP 组播/多播实现步骤

UDP组播 实现服务器对客户端分组播放(发送数据). 实现步骤: server.c 1.创建服务器套接字 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);   返回套接字 2.构建服务器地址结构 struct sockaddr_in serveraddr; #include <strings.h> bzero(&server

QT的UDP组播技术

一 UDP介绍 UDP是一种简单轻量级的传输层协议,提供无连接的,不可靠的报文传输.适合下面4种情况: 网络数据大多为短消息. 拥有大量客户端. 对数据安全性无特殊要求 网络负担非常重,但对响应速度要求高. 二 基于udp协议的c/s编程模型(广播),组播的话,明天再看. 客户端: class Sender : public QObject { Q_OBJECT //可以使用信号和槽 public: explicit Sender(QObject *parent = 0); ~Sender();

UDP组播相关

Windows服务端 #include "stdafx.h" #include <winsock2.h> #include <ws2tcpip.h> #include <stdio.h> #include <stdlib.h> #define MCASTADDR "239.255.255.239" //本例使用的多播组地址. #define MCASTPORT 9111 //绑定的本地端口号. #define BUFS

QT Udp组播(穿透)

http://blog.csdn.net/victoryknight/article/details/7814243 主题 UDPQt路由器 局域网内的两台机器如果隔有路由器,那么这两台机器之间不能进行广播通信,但是我们可以换成组播的通信的方式,达到相互通信的效果. 一 服务器端 QUdpSocket udp_socket; udp_socket.bind(QHostAddress::Any, udp_listen_port, QUdpSocket::ReuseAddressHint); QHo

ios UDP组播

这几天在做wifi的智能灯,需要用到组播的知识就把这个记录一下,服务端使用java写的,代码如下: 1 import java.io.IOException; 2 import java.net.DatagramPacket; 3 import java.net.DatagramSocket; 4 import java.net.InetAddress; 5 import java.net.MulticastSocket; 6 7 class UDPServer{ 8 public static