项目中有这样一项需求:服务器上主机上安装的有程序打开服务、三维演示程序,屏幕拓展到了幕墙大屏;水晶位于舞台一侧,演讲者需要通过服务器的服务打在主机上的程序,并实现演示操作。
要完成上述需求就需要用到一个功能:远程桌面控制。
远程桌面协议有三个:VNC/SPICE/RDP。下面的连接介绍了这三种协议的特点和区别。
https://blog.csdn.net/caoshangpa/article/details/75206195
由于都是windows系统,我首先接触了rdp协议。从网上直接找的轮子。但是打开demo测试了一下,效果不是很好,首先是鼠标有延迟,使用效果不好,更重要的是rdp只支持一个设备远程连接 而且远界面会出现锁屏,就是我把桌面远程到了水晶讲台,结果服务器主机锁屏了,幕墙也锁屏了就无法对观众进行演示。
https://blog.csdn.net/wochendaixin/article/details/78465095
这两点让我直接选择了VNC的控件。
VNC(Virtual Network Computing)是基于RFB(Remote Frame Buffer)协议进行通信的,是一个基于平台无关的简单显示协议的超级瘦客户系统。VNC最初有某个实验室开发出来,后来由RealVNC公司负责维护和更新。RealVNC公司自己开发了一套客户端/服务端程序和云服务,个人的云服务好像是免费。而且还封装了一套多种语言的SDK供开发者使用,但是收费。
开发包中提供了安卓、HTML、C#、python语言的服务端可客户端Demo。
我使用的是C#代码编写的WPF程序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qgXtegKa-1589344465949)(https://i.imgur.com/CRi7mWX.png)]
WPF的vnc样例的项目如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IbBJ4kDu-1589344465956)(https://i.imgur.com/R7xmlZS.png)]
ConnectSettings.cs:连接字符类,主要字段有云连接地址、密码,tcp连接的账号密码等。这个类在程序初始化的时候用来进行服务端连接。
ConnectSettingsWindow.xaml是用来连接服务端的窗口页面。
MainWindow.xaml是显示服务端桌面的主窗体,页面上包含了是否批改、颜色、鼠标控制等按钮。点击“connect”后 vnc控件容器便会开始播放。
ViewerControl.cs是realvnc封装的winform播放控件,是播放画面的核心部分。
VncKeyMapper.cs将客户端的键盘对服务端的键盘进行映射。
VncWinformEventMap.cs 对上面的键盘映射进行注册,通过按键事件注册才能在客户端敲击某一个键的时候服务端有响应。
VncLibraryThread.cs:加载和初始化库文件、开启客户端和服务端的会话、RunLoop这个我不知道怎么解释,大概意思就是一直进行loop里的操作,直到外部改变loop的值
--------------------------撰写分割线------------------------------
VncLibraryThread.cs:
ConnectSettingsWindow:负责创建一个Viwer类
MainWindow:
开启连接:设定viwer会话设置,包括连接属性和注册OnConnect、OnDisconnect、OnNewStatus事件、绑定按键事件、在线程中开启一个会话
显示状态和信息、更改vnc的功能等
VncViewerSession:
给对象查看器Viwer设置回调函数:
ConnectionCallback:connected、disconnected、
FramebufferCallback(画面帧获取后触发)、ServerEventCallback(服务事件)AnnotationManagerCallback(注释事件)
AuthenticationCallback(权限事件)
使用tcp连接:
定义连接器tcpConnector开启连接
更细的我还没研究透。我只说下我是如何使用的,以及大致的逻辑过程
MainWindow初始化。位置:MainWindow.cs
VncLibraryThread.Start();//线程启动
LoadLibrary加载库文件。位置:VncLibraryThread.cs
LibraryInit()//库文件初始化。就是加载根目录下的lib文件夹,将win-x86文件夹里的几个文件包含到项目里。这个地方要是移植debug程序时候记得改一下路径。在初始化的时候注意,要去realvnc官网试用/购买企业版,获得一个附加码,没有附加码是不能加载库文件的。
//if (!string.IsNullOrEmpty(DirectTcpAddOnCode))
Library.EnableAddOn(DirectTcpAddOnCode);
private const string DirectTcpAddOnCode = @“V7mU+pv3mWBpl/NfRad4HSp6/GI7aUhOoikI0eAGCrvM+RTepIZQNnEJQ7Tcn3sT
WQtP3kE3G95jSKzLGX+U9sbac8kvZhJx5YMa2xUryPE0CwOfI3jJg3woprA2MB3b
scIHvUWbDHAtpB7x3dfWXjQbidSkJkBSMl9Z/NvvPOFzdW2Ua9mvAT2eimqfY/YX
3O8NKWNyzdk9xG7TqjVWpo2QexGrFgd81TAIDvPqh2sgrL+KW69s/96fmJKfqd6+
u/kuEhxhQj4Xx8e0H0Pej6y/EYP5MnD+lcLzhw3wM2IrDUjGKPL81wgVxcbgUmDd
Y1Qr4jSmvtr1F6bq54ZVLjao9ppaOkF8S6xW63dnOllUSs0BDsCvUyDvc4nY/Ii0
ztQj2N1OLMka3ajTMHUdEDMzpITgkMB6lz57nIYejcThRXeLxEjnxEy/AAl3I49l
Cor+T98MfnZa7iw3nMdM0hMrdAyDdOq63yByQ2iCNqSbBXaP6+tLO+ottLttf9TK
STY29jWOC+5c1IseRVoxAd8ZiGeCEmzfa3e0OTnAZMBsPzdQS05dF/J3pFHhLrez
eA5yD/EvMRhd2QHSQk8oz27ZyOujXo0SlgfkTdOY52gPYfjsVv70JTSQCb4FgNMX
i9HByrnRMquQI1KRoy0QUHk0R8qbybD2JXMGsVEIfX4=”;
RunLoop,线程开启新回话或者使线程等待以便于终止。
点Connect=>Direct Tcp connection,输入服务端ip和端口号,使用默认的5900.StartConnection()开启连接。位置:MainWindow.cs
StartConnection();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4i2z2rS-1589344465960)(https://i.imgur.com/mk3go4k.png)]
5. 设定viewerSession属性;设定按键事件VncWinformEventMap; 启动会话StartViewerSession; 位置:MainWindow.cs=> StartConnection()
viewerSession = new VncViewerSession
{
LocalCloudAddress = ConnectSettings.LocalCloudAddress,
LocalCloudPassword = ConnectSettings.LocalCloudPassword,
PeerCloudAddress = ConnectSettings.PeerCloudAddress,
TcpAddress = ConnectSettings.DirectTcpIpAddress,
TcpPort = ConnectSettings.DirectTcpPort,
UsingCloud = ConnectSettings.UsingCloud,
AnnotationEnabled = true,
FrameBufferHandler = VncViewerControl,
OnConnect = () => DisplayMessage("Connected"),
OnDisconnect = (msg, flags) =>
Dispatcher.BeginInvoke(new Action(() => OnDisconnect(msg))),
// Put status messages on the status-label
OnNewStatus = (msg) => DisplayMessage(msg),
CurrentCanvasSize = VncViewerControl.Size
};
EventMap = new VncWinformEventMap(viewerSession, VncViewerControl);
// Give the viewer session user-input events
if (KeyboardControl.IsChecked == true)
EventMap.RegisterKeyboardControls(true);
if (MouseControl.IsChecked == true)
EventMap.RegisterMouseControls(true);
ManageConnectControls(true);
// Give the child-control the focus of keyboard
VncViewerControl.Focus();
VncLibraryThread.StartViewerSession(viewerSession);
ViewerSession = viewerSession;
跳进Runloop开启一个新会话 范围:VncLibraryThread的RunLoop
while (WaitHandle.WaitAny(handles) == 0) // NewSession
{
// Reset the event, so that the UI may start a new session
// as soon as it is notified of this session’s disconnection.
NewSession.Reset();
//
// Do all the work in here!
//
CurrentVncViewerSession.Run();
}
设置回调函数、调整帧页面大小、连接服务器。连接了以后就会线程运行OnFrameBufferUpdated绘制GUI、控件绘制ViewerControl_Paint之类的。位置:ViewerSession.cs
using (DirectTcpConnector tcpConnector = new DirectTcpConnector())
{
tcpConnector.Connect(TcpAddress, TcpPort, connectionHandler);
}`
程序弹出一个回调页面,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogzjvMjk-1589344465967)(https://i.imgur.com/zy3YGaq.png)]如果ok不能点击,可以换个电脑试一下,我真不知道为什么在我的工作pc上不能用
点击OK后会再回调一个输入账号密码的窗口,登录即可。
这个页面可以重写。由于我只需要点击按钮直接登录服务器,因此我做了以下操作:
1)设定对等验证回调函数(直译),并注册
PeerVerificationCallback = new Viewer.PeerVerificationCallback(
verifyPeer:(Viewer,hexFingerprint,catchphraseFingerprint,serverRsaPublic) => {
Viewer.SendPeerVerificationResponse(true);//当出现OK/Cancel窗口时 直接选择ok;hexFingerprint是服务器签名,catchphraseFingerprint什么Catchphase信息、
});
viewer.SetPeerVerificationCallback(PeerVerificationCallback);
设定权限验证回调函数,并注册
AuthenticationCallback = new Viewer.AuthenticationCallback(
requestUserCredentials:(Viewer, needUser, needPasswd) => {
Viewer.SendAuthenticationResponse(true, null,“123456”); //发送账号密码, 是否需要账号密码跟realvnc服务端的设置有关。我设置的是只要vnc密码
});
viewer.SetAuthenticationCallback(AuthenticationCallback);
我最开始学习realvnc是想着服务端有现成的realvncserver(自己找序列号破一下),做的相当好,sdk也是他们封装好的,简单方便效果好。但是我做好之后 跟领导一说 要55欧元一年,领导说再研究下开源的vnc控件吧。我一个同事说 上家公司用vnc做汽车面板的手机远程桌面,可以使用批量的,会很便宜。反正我接着就是研究开源的vnc。
---------------------撰写分割线-------------------------
在网上找了找,资源虽然不多但还是有的。
下载了好多demo程序都不是很好。在多方尝试之后,我用“NVNC-master”里面提供的代码开发了客户端,使用Github上的一个仓库“vncFling2.0”搭建了vnc的客户端/服务端。
为什么选择两个组合呢?!
“NVNC-master”这个代码包里其实有服务端可客户端demo代码,但是页面效果很差,延迟有些高。我将服务端换成“vncFling2”,效果好了很多。
但是“vncFling2”没有代码 只有程序。因此我就直接偷懒,将服务端代码直接封装到一个程序中,到时候把服务端调用起来就ok了。
-----------------------分割线------------------------------
还没写完……