C# 使用UDP組播實現區域網路桌面共用

來源:互聯網
上載者:User

標籤:style   blog   http   io   color   ar   os   使用   sp   

轉: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組播實現區域網路桌面共用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.