C#实现屏幕墙:同时监控多个电脑桌面(支持Windows、信创Linux、银河麒麟、统信UOS)

  最近有不少的客户提到了安防监控等场景,需要满足跨平台、高实时性的多个屏幕的监控需求,用户可在监控端实时查看多个被监控电脑屏幕的内容,即类似屏幕墙的需求。于是,我用C#实现了一个屏幕墙Demo分享给大家。

该Demo解决方案一共包括2个项目:服务端、PC客户端,都是基于.NET Core 3.1 。

  监控端运行时主界面如下所示:

  

  Demo的主要功能如下:

(1)客户端登录时,可以选择登录身份:监控端、被监控端。

(2)服务端和客户端都可以运行在Windows、Linux 和 国产OS(如银河麒麟、统信UOS)上。

(3)被监控端以托管服务的方式运行。

(4)在监控端可以看到所有在线的被监控端的屏幕,并可选择每行显示的屏幕个数。

(5)在监控端,双击每个屏幕视图宫格,将浮出大窗口来显示目标屏幕图像。

接下来,我将给大家介绍整个功能的实现原理和代码逻辑,大家可以从文末下载源码后,对照源码再来看下面的介绍就会更清晰些。

一.服务端实现  

首先,我们需要在一个公共的类库 VideoWall.Core 中,来定义客户端与服务端之间交互的消息类型:

复制代码
    /// <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; 
    }

  然后,我们来编写服务端 VideoWall.Server 的代码,其主要是将被监控端的上下线通知给监控端,实现起来很简单,这里不做过多的介绍,其关键核心代码只有几句,就是创建 OMCS 多媒体服务器实例,预定用户上下线事件。

复制代码
//创建多媒体服务器实例
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);
//收到来自客户端的自定义消息
MultimediaServer.CustomizedMessageReceived += MultimediaServer_CustomizedMessageReceived

  服务端要处理的来自客户端的自定义消息,主要就是监控端上线时,请求所有在线的被控端列表:

复制代码
        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);
            }
        }

  服务端运行界面如下所示:

   

二.PC客户端实现

  客户端中我们也分为了2种身份:监控端、被监控端(本文使用监控端身份登录)。

  

  我们在登录时,需要初始化 OMCS 的多媒体管理器 来连接服务端进行通信,其实也很简单,我们也只需要调用几句话就OK。

复制代码
 //是否监控端账号
 isMonitor = monitor;
 //计算机名称
 string computerName = Environment.MachineName;
 string token = isMonitor ? GlobalConsts.MonitorToken : GlobalConsts.TargetToken;
 string id = token + computerName;
 //登录到OMCS服务器
 IMultimediaManager multimediaManager = MultimediaManagerFactory.GetSingleton();
 multimediaManager.Initialize(id, "", ConfigurationManager.AppSettings["ServerIP"], 9900);

   为了简单起见,Demo中我们通过登录账号的前缀来区分监控端和被监控端:  

复制代码
   /// <summary>
   /// 全局常量
   /// </summary>
   public class GlobalConsts
   {
       /// <summary>
       /// 监控方账号前缀
       /// </summary>
       public const string MonitorToken = "#";

       /// <summary>
       /// 被监控方账号前缀
       /// </summary>
       public const string TargetToken = ":";
   }

   登录成功后,先获取所有被控端列表,然后通过CustomizedMessageReceived处理被监控端的上下线逻辑。

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

服务端收到该请求后,会从内存拿到所有在线的被监控端的列表,然后也是通过InformationType.GetAllTargetID消息类型,将回复内容发送给请求端。这个过程已经在上面的服务端实现代码中介绍过了。

   接下来是客户端收到来自服务端的请求回复以及其它被监控端上下线的通知的处理过程。

复制代码
/// <summary>
/// 收到来自服务器或其它客户端的自定义消息
/// </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 方法的实现是关键,它控制着监控页面的宫格布局显示。

  比如,当有被监控端上线时,监控端就会new一个桌面连接器DynamicDesktopConnector ,来连接对方的桌面,这样就可以看到对方的屏幕图像了,具体代码如下所示:

复制代码
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)
            {
                //延时连接,避免对方设备管理器还未完成初始化
                Thread.Sleep(1000);
            }
            connector.BeginConnect(destID);//开始连接目标桌面
        });
    }
    return connector;
}

  同样的道理,当某个被监控端下线时,就会断开其对应的桌面连接器DynamicDesktopConnector,并且在UI上将其从容器中移除。具体代码请参见源码,这里就不赘述了。

三. 源码下载

  上面只是讲了几个重点,并不全面,大家下载下面的源码可以更深入的研究。

  服务端与PC端源码:VideoWall.rar  

  最后说明一下与性能相关的疑问:如果同时监控了很多台电脑的屏幕,那么运行监控端的电脑的CPU、内存、GPU,以及带宽能扛得住吗?

嗯,这是个很好的问题,OMCS 有个按需自动调整屏幕的输出分辨率的功能就可以完美地解决这一问题,即OMCS的Owner端可以根据观看方的窗口大小来自动调整输出的屏幕图像的分辨率,这将极大地节省CPU/GPU、内存和带宽资源。比如某个被监控端的显示器的分辨率是4K高清的(3840*2160),但是,其图像在监控端观看时,仅仅显示在一个640*360的宫格中,那么,被监控端会将4K图像等比缩放为640*360后,再编码压缩发送给监控端。

  所有,有了这个功能作为基础,同时监控十数台电脑的屏幕都是可以的。如果被监控端的数目更多,我们还可以加上分页观看的功能。

相关推荐
贝锐3 天前
金仓、达梦、TDSQL全适配:向日葵补齐信创数据库层远程运维能力
运维·数据库·信创
CodeHackerBhx6 天前
MySQL 迁达梦踩坑实录:FIND_IN_SET、timestamp(6) 和一整批要改的函数
数据库·mysql·达梦·信创·数据库迁移
RuoyiOffice8 天前
SpringBoot3 + openGauss:RuoYi Office 登录、流程发布与用印填报适配实战
java·数据库·spring boot·opengauss·信创
AI开源软件10 天前
SpringBoot信创项目怎么验收?从数据库迁移到交付证据
postgresql·springboot·达梦·opengauss·信创·企业管理系统
数据库小学妹10 天前
空间数据库 PostGIS 兼容性怎么验?函数、类型、坐标系逐条核(附 SQL)
gis·信创·空间分析·空间数据库·地图渲染
RuoyiOffice11 天前
达梦 DM8 + openGauss:RuoYi Office 的 MySQL 数据转换与导入实践
数据库·spring boot·python·sql·信创
RuoyiOffice12 天前
SpringBoot OA推荐:面向信创选型,看看这套企业协同平台
spring boot·vue3·信创·flowable·企业协同·ruoyi office·oa选型
正在走向自律13 天前
数据集成产品的技术底座:从日志解析原理到 KFS 同步链路搭建(含建表与增删改查完整代码)
数据库·oracle·信创·数据集成·国产数据库·kingbasees·全量+增量同步
handonch15 天前
金仓 KingbaseES 时区问题根治指南:从「总是差 8 小时」到全链路统一
linux·docker·postgresql·信创
数据库小学妹18 天前
数据库运维怎么做:五级成熟度对照与落地清单
信创·数据库高可用·数据库监控·数据库运维·数据库运维管理·数据库备份恢复