最近有不少的客户提到了安防监控等场景,需要满足跨平台、高实时性的多个屏幕的监控需求,用户可在监控端实时查看多个被监控电脑屏幕的内容,即类似屏幕墙的需求。于是,我用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后,再编码压缩发送给监控端。

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

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

  1. k8s中使用prometheus operator监控外部服务器部署的windows exporter

    k8s中使用prometheus operator监控外部服务器部署的windows exporter 0.文档说明 (1)Prometheus Operator是一个流行的k8s集群监控套件,项目地 ...

  2. server宕机监控、检測、报警程序(139绑定手机短信报警)monitor_down.sh

    宕机监控报警程序 一.   需求来源 宕机对运维人员来说,最痛苦了.怎样检測一台server是否还在正常执行,假设该server宕机,怎样在第一时间监測到并通知一线运维人员进行维护,最大化降低损失. ...

  3. Zabbix监控系统配置

    1.Zabbix是一个基于WEB界面的提供分布式系统监控的企业级的开源解决方案 Zabbix能监视各种网络参数,保证服务器系统的安全稳定的运行,并提供灵活的通知机制以让SA快速定位并解决存在的各种问题 ...

  4. 搭建ARL资产安全灯塔

    老年人了,只能靠安装部署项目混混日子这样~ 简介: 斗象TCC团队正式发布「ARL资产安全灯塔」开源版,该项目现已上线开源社区GitHub.ARL旨在快速侦察与目标关联的互联网资产,构建基础资产信息库 ...

  5. Nifi:nifi的基本使用

    Nifi的安装使用 Nifi安装 首先说一下Nifi的安装,这里Nifi可以支持Windows版和Linux,只需要去官网:http://nifi.apache.org/ 根据自己需要的版本,选择下载 ...

  6. Python集成开发工具(IDE)推荐

    1.7 Python集成开发工具(IDE)推荐 1.7.1 Notepad++ Notepad++是Windows操作系统下的一套文本编辑器(软件版权许可证: GPL),有完整的中文化接口及支持多国语 ...

  7. 【开源】.net 分布式架构之监控平台

    开源地址:http://git.oschina.net/chejiangyi/Dyd.BaseService.Monitor .net 简单监控平台,用于集群的性能监控,应用耗时监控管理,统一日志管理 ...

  8. 利用WCF的双工通讯实现一个简单的心跳监控系统

    何为心跳监控系统? 故名思义,就是监控某个或某些个程序的运行状态,就好比医院里面的心跳监视仪一样,能够随时显示病人的心跳情况. 心跳监控的目的是什么? 与医院里面的心跳监视仪目的类似,监控程序运行状态 ...

  9. 如何灵活运用Linux 进程资源监控和进程限制

    导读 每个 Linux 系统管理员都应该知道如何验证硬件.资源和主要进程的完整性和可用性.另外,基于每个用户设置资源限制也是其中一项必备技能. 在这篇文章中,我们会介绍一些能够确保系统硬件和软件正常工 ...

  10. Nagios监控平台搭建

    Nagios是一款开源的免费网络监视工具,能有效监控Windows.Linux和Unix的主机状态,交换机路由器等网络设置,打印机等.在系统或服务状态异常时发出邮件或短信报警第一时间通知网站运维人员, ...

随机推荐

  1. Ribbon过滤器原理解析

    Ribbon过滤器整体看是一个矩阵构建与矩阵乘法,RocksDB中对它的实现是进行了合理的空间.时间上的优化的. 符号 整个过滤器都和矩阵计算CS=R相关,C是\(n*n\)矩阵,S是\(n*m\)矩 ...

  2. 记一次SSD性能瓶颈排查之路——寿命与性能之间的取舍

    1. 背景  我就职于一家轨道交通行业公司,负责的产品之一是日志记录板卡配套软件.有一天接到了现场报告,记录软件出现通信异常,将日志数据拉回来以后,发现出现异常时,CPU使用率接近100%,记录相关软 ...

  3. Java集合源码--ArrayList的可视化操作过程

    关于ArrayList的元素插入.检索.修改.删除.扩容等可视化操作过程 还有关于ArrayList的迭代器.线程安全和时间复杂度 1. 底层数据结构 基于动态数组实现,内部维护一个Object[]数 ...

  4. 把数据库表的信息添加到list集合里面

    把数据库表里面的信息添加到集合里面并且打印出来: 数据库表的内容:  java代码逻辑处理: 1 public static void main(String[] args) { 2 3 Connec ...

  5. setAttr和getPara() 用法注意~

    在一个新增的add.html 需要把一个参数传给save后台方法.这个参数是从别的页面获得的,需要setAttr才能在add.html看到值. 如果想再从后台拿到该值必须,与数据库的对应字段名称一样才 ...

  6. C# Json 解析,针对数字开头变量Json字符串转模型(Model)

    namespace 解析数字开头的变量JSON { class Program { static void Main(string[] args) { string strJson = "{ ...

  7. leetcode 918

    简介 环形数组的最大子数组的和的最大值. 思路 分两种情况讨论, 一种是最大子数组就是普通值, 那么只要求出正常值就可以了. 另一种情况是除去全局最小的中间一段, 然后就是最大值. code clas ...

  8. jlink + jz2440 + win10 操作方式

    ### 重新烧写 jlink uboot.bin `http://www.jz2440.com/started/j-flash/nor-flash烧写-以uboot为例` 1.第一阶段  通过 jli ...

  9. Piecewise Smooth Subdivision Surfaces with Normal Control 未完待续

    简介 参考链接 https://mrl.cs.nyu.edu/publications/piecewise-smooth/ 相关pdf和代码, 尝试编译了一下, 但是有太多的错误, 遂放弃

  10. java combobox 多选框

    简介 简单 code package calcu; import java.awt.*; import javax.swing.*; public class ComboBoxFrame extend ...