最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于C#實(shí)現(xiàn)屏幕墻并同時(shí)監(jiān)控多個(gè)電腦桌面

 更新時(shí)間:2025年08月20日 08:20:10   作者:zhuweisky  
這篇文章主要為大家詳細(xì)介紹了如何基于C#實(shí)現(xiàn)一個(gè)屏幕墻,用戶可在監(jiān)控端實(shí)時(shí)查看多個(gè)被監(jiān)控電腦屏幕的內(nèi)容,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

引言

最近有不少的客戶提到了安防監(jiān)控等場(chǎng)景,需要滿足跨平臺(tái)、高實(shí)時(shí)性的多個(gè)屏幕的監(jiān)控需求,用戶可在監(jiān)控端實(shí)時(shí)查看多個(gè)被監(jiān)控電腦屏幕的內(nèi)容,即類似屏幕墻的需求。于是,我用C#實(shí)現(xiàn)了一個(gè)屏幕墻Demo分享給大家。

該Demo解決方案一共包括2個(gè)項(xiàng)目:服務(wù)端、PC客戶端,都是基于.NET Core 3.1 。 

監(jiān)控端運(yùn)行時(shí)主界面如下所示:   

Demo的主要功能如下:

(1)客戶端登錄時(shí),可以選擇登錄身份:監(jiān)控端、被監(jiān)控端。

(2)服務(wù)端和客戶端都可以運(yùn)行在Windows、Linux 和 國(guó)產(chǎn)OS(如銀河麒麟、統(tǒng)信UOS)上。

(3)被監(jiān)控端以托管服務(wù)的方式運(yùn)行。

(4)在監(jiān)控端可以看到所有在線的被監(jiān)控端的屏幕,并可選擇每行顯示的屏幕個(gè)數(shù)。

(5)在監(jiān)控端,雙擊每個(gè)屏幕視圖宮格,將浮出大窗口來(lái)顯示目標(biāo)屏幕圖像。

接下來(lái),我將給大家介紹整個(gè)功能的實(shí)現(xiàn)原理和代碼邏輯,大家可以從文末下載源碼后,對(duì)照源碼再來(lái)看下面的介紹就會(huì)更清晰些。

服務(wù)端實(shí)現(xiàn)

首先,我們需要在一個(gè)公共的類庫(kù) VideoWall.Core 中,來(lái)定義客戶端與服務(wù)端之間交互的消息類型: 

    /// <summary>
    /// 自定義消息類型 InformationTypes
    /// </summary>
    public class InformationType
    { 
        /// <summary>
        /// 獲取所有被控端列表
        /// </summary>
        public static int GetAllTargetID = 1001;

        /// <summary>
        /// 被控端上線通知
        /// </summary>
        public static int TargetOnline = 1002;

        /// <summary>
        /// 被控端下線通知
        /// </summary>
        public static int TargetOffline = 1003; 
    }

然后,我們來(lái)編寫服務(wù)端 VideoWall.Server 的代碼,其主要是將被監(jiān)控端的上下線通知給監(jiān)控端,實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,這里不做過(guò)多的介紹,其關(guān)鍵核心代碼只有幾句,就是創(chuàng)建 OMCS 多媒體服務(wù)器實(shí)例,預(yù)定用戶上下線事件。

//創(chuàng)建多媒體服務(wù)器實(shí)例
Program.MultimediaServer = MultimediaServerFactory.CreateMultimediaServer(int.Parse(ConfigurationManager.AppSettings["Port"]), new DefaultUserVerifier(), bool.Parse(ConfigurationManager.AppSettings["SecurityLogEnabled"]));
//客戶端上線通知
MultimediaServer.UserConnected += new ESBasic.CbGeneric<string>(multimediaServer_UserConnected);
//客戶端掉線通知
MultimediaServer.UserDisconnected += new ESBasic.CbGeneric<string>(multimediaServer_UserDisconnected);
//收到來(lái)自客戶端的自定義消息
MultimediaServer.CustomizedMessageReceived += MultimediaServer_CustomizedMessageReceived

服務(wù)端要處理的來(lái)自客戶端的自定義消息,主要就是監(jiān)控端上線時(shí),請(qǐng)求所有在線的被控端列表:

        private static void MultimediaServer_CustomizedMessageReceived(string userID, int informationType, byte[] bytes, string tag)
        { 
            if(informationType == InformationType.GetAllTargetID)
            {
                byte[] data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(TargetList));
                MultimediaServer.SendCustomizedMessage(userID, InformationType.GetAllTargetID, data, null);
            }
        }

服務(wù)端運(yùn)行界面如下所示:  

PC客戶端實(shí)現(xiàn)

客戶端中我們也分為了2種身份:監(jiān)控端、被監(jiān)控端(本文使用監(jiān)控端身份登錄)。

我們?cè)诘卿洉r(shí),需要初始化 OMCS 的多媒體管理器 來(lái)連接服務(wù)端進(jìn)行通信,其實(shí)也很簡(jiǎn)單,我們也只需要調(diào)用幾句話就OK。 

 //是否監(jiān)控端賬號(hào)
 isMonitor = monitor;
 //計(jì)算機(jī)名稱
 string computerName = Environment.MachineName;
 string token = isMonitor ? GlobalConsts.MonitorToken : GlobalConsts.TargetToken;
 string id = token + computerName;
 //登錄到OMCS服務(wù)器
 IMultimediaManager multimediaManager = MultimediaManagerFactory.GetSingleton();
 multimediaManager.Initialize(id, "", ConfigurationManager.AppSettings["ServerIP"], 9900);

為了簡(jiǎn)單起見,Demo中我們通過(guò)登錄賬號(hào)的前綴來(lái)區(qū)分監(jiān)控端和被監(jiān)控端:  

   /// <summary>
   /// 全局常量
   /// </summary>
   public class GlobalConsts
   {
       /// <summary>
       /// 監(jiān)控方賬號(hào)前綴
       /// </summary>
       public const string MonitorToken = "#";

       /// <summary>
       /// 被監(jiān)控方賬號(hào)前綴
       /// </summary>
       public const string TargetToken = ":";
   }

登錄成功后,先獲取所有被控端列表,然后通過(guò)CustomizedMessageReceived處理被監(jiān)控端的上下線邏輯。

/// <summary>
/// 獲取所有被控端列表
/// </summary>
private void GetAllTargetID()
{
    this.multimediaManager.SendCustomizedMessage("_0", InformationType.GetAllTargetID, null, null);
}

服務(wù)端收到該請(qǐng)求后,會(huì)從內(nèi)存拿到所有在線的被監(jiān)控端的列表,然后也是通過(guò)InformationType.GetAllTargetID消息類型,將回復(fù)內(nèi)容發(fā)送給請(qǐng)求端。這個(gè)過(guò)程已經(jīng)在上面的服務(wù)端實(shí)現(xiàn)代碼中介紹過(guò)了。

接下來(lái)是客戶端收到來(lái)自服務(wù)端的請(qǐng)求回復(fù)以及其它被監(jiān)控端上下線的通知的處理過(guò)程。

/// <summary>
/// 收到來(lái)自服務(wù)器或其它客戶端的自定義消息
/// </summary> 
private void MultimediaManager_CustomizedMessageReceived(string userID, int informationType, byte[] bytes, string tag)
{
    if (informationType == InformationType.GetAllTargetID)
    {
        string str = Encoding.UTF8.GetString(bytes);
        List<string> targetList = JsonConvert.DeserializeObject<List<string>>(str);
        foreach (string targetID in targetList)
        {
            UserStatusChange(targetID, true, false);
        }
        return;
    }
    if (informationType == InformationType.TargetOnline)
    {
        string targetID = Encoding.UTF8.GetString(bytes);
        UserStatusChange(targetID, true, true);
        return;
    }
    if (informationType == InformationType.TargetOffline)
    {
        string targetID = Encoding.UTF8.GetString(bytes);
        UserStatusChange(targetID, online: false,true);
        return;
    }
}

UserStatusChange 方法的實(shí)現(xiàn)是關(guān)鍵,它控制著監(jiān)控頁(yè)面的宮格布局顯示。

比如,當(dāng)有被監(jiān)控端上線時(shí),監(jiān)控端就會(huì)new一個(gè)桌面連接器DynamicDesktopConnector ,來(lái)連接對(duì)方的桌面,這樣就可以看到對(duì)方的屏幕圖像了,具體代碼如下所示:

internal DynamicDesktopConnector AddConnector(string destID,bool delayConnection)
{
    DynamicDesktopConnector connector = desktopConnectorManager.Get(destID);
    if (connector == null)
    {
        connector = new DynamicDesktopConnector();
        connector.VideoDrawMode = VideoDrawMode.Fill;       
        connector.ConnectEnded += Connector_ConnectEnded;
        connector.Disconnected += Connector_Disconnected;
        connector.NewFrameReceived += Connector_NewFrameReceived;
        this.desktopConnectorManager.Add(destID, connector);
        Task.Factory.StartNew(() => {
            if (delayConnection)
            {
                //延時(shí)連接,避免對(duì)方設(shè)備管理器還未完成初始化
                Thread.Sleep(1000);
            }
            connector.BeginConnect(destID);//開始連接目標(biāo)桌面
        });
    }
    return connector;
}

同樣的道理,當(dāng)某個(gè)被監(jiān)控端下線時(shí),就會(huì)斷開其對(duì)應(yīng)的桌面連接器DynamicDesktopConnector,并且在UI上將其從容器中移除。具體代碼請(qǐng)參見源碼,這里就不贅述了。 

總結(jié)

最后說(shuō)明一下與性能相關(guān)的疑問:如果同時(shí)監(jiān)控了很多臺(tái)電腦的屏幕,那么運(yùn)行監(jiān)控端的電腦的CPU、內(nèi)存、GPU,以及帶寬能扛得住嗎?

嗯,這是個(gè)很好的問題,OMCS 有個(gè)按需自動(dòng)調(diào)整屏幕的輸出分辨率的功能就可以完美地解決這一問題,即OMCS的Owner端可以根據(jù)觀看方的窗口大小來(lái)自動(dòng)調(diào)整輸出的屏幕圖像的分辨率,這將極大地節(jié)省CPU/GPU、內(nèi)存和帶寬資源。比如某個(gè)被監(jiān)控端的顯示器的分辨率是4K高清的(3840*2160),但是,其圖像在監(jiān)控端觀看時(shí),僅僅顯示在一個(gè)640*360的宮格中,那么,被監(jiān)控端會(huì)將4K圖像等比縮放為640*360后,再編碼壓縮發(fā)送給監(jiān)控端。

所有,有了這個(gè)功能作為基礎(chǔ),同時(shí)監(jiān)控十?dāng)?shù)臺(tái)電腦的屏幕都是可以的。如果被監(jiān)控端的數(shù)目更多,我們還可以加上分頁(yè)觀看的功能。

到此這篇關(guān)于基于C#實(shí)現(xiàn)屏幕墻并同時(shí)監(jiān)控多個(gè)電腦桌面的文章就介紹到這了,更多相關(guān)C#屏幕墻內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

横峰县| 福建省| 新竹县| 三明市| 酒泉市| 新龙县| 大英县| 大冶市| 兰溪市| 丰镇市| 沙雅县| 都兰县| 治县。| 黔西县| 北安市| 河西区| 丹寨县| 黄梅县| 襄汾县| 高州市| 永兴县| 当阳市| 邹平县| 通化县| 巫山县| 曲沃县| 凌云县| 高台县| 婺源县| 琼结县| 开鲁县| 全南县| 苏尼特左旗| 嘉黎县| 柳江县| 平湖市| 邵阳县| 富锦市| 凤冈县| 称多县| 尼勒克县|