从产品需求侧讲,一个VDI的产品具备如下的一些主要特性。
移动设备支持 |
终端管理数量限制 |
文件传输 |
远程重启 |
远程打印 |
局域网唤醒 |
客户端自定义别名 |
定制品牌 |
RestfulAPI |
地址簿 |
会话录制 |
命令行界面 |
会话记忆 |
双重安全认证 |
端到端加密 |
禁用自动更新 |
自动断开 |
隐私模式 |
用户管理 |
白板 |
文字聊天 |
会话邀请 |
本文不会针对这些产品功能做具体开发的介绍,也不包含包括VDS相关的一些概念,内容的涉及。
而是着重在最基础的能力,虚拟化显示器,桌面环境,和webrtc呈现流转方面,没有提供很多代码,主要是设计思路和方法。从而具备一个VDI基础的核心实现。从这个出发点来讲,我们的VDI Demo具备以下的主要特性
- 虚拟化显示器
- 虚拟化桌面内容
- 传递远程服务器的鼠标和键盘信息
- 网络自适应下的图像适配
- 多用户下的状态隔离
一 虚拟化显示器
由于我们的VDI产品服务器端程序,运行在服务器端,然而往往服务器端是不安装实际显示器的。
此时有几种方案,虚拟整个操作系统,或者只虚拟显示器内容。我们采用后者,这样成本小一些。
目前这方面涉及到驱动级别的虚拟化,开源的方案有很多。下面是一个我们体验过后的比较列表。
Project | Iddcx version | Signed | Gaming | HDR | H-Cursor | Tweakable | Controller |
usbmmidd_v2 | 未曾尝试 | ✅ | ❌ | ❌ | ❌ | ||
IddSampleDriver | 可以用出现显示器了。需要几个添加几个驱动即可 | 1.2 | ❌ | ❌ | ❌ | ❌ | |
RustDeskIddDriver | 太繁琐了。demo编译太累 | 1.2 | ❌ | ❌ | ❌ | ❌ | |
Virtual-Display-Driver (HDR) | 可以用。出现显示器了。需要几个添加几个驱动即可 | 1.10 | ❌ | ✅ | ❌ | ||
virtual-display-rs | 安装OK,运行错误 | 1.5 | ❌ | ❌ | ❌ | ✅ | |
parsec-vdd | 独立DEMO达到预期,需要几个自己添加几个,然后系统显示器配置具体行为即可 | 1.4 | ✅ | ✅ | ❌ | ✅ | 🆗 |
实际上按照你的情况选择对应的需求。
但基本上都是基于IDD的那一套。
用这个方案的思路是,模拟出来真实的显示器,从而让操作系统在绘制系统层确保屏幕输出的内容。使得我们第二部的虚拟远程桌面内容得以低成本实现。
二 虚拟化桌面内容
VDI实际显示的内容就是远程不管是多个还是单个用户桌面空间的内容,所以虚拟化显示远程桌面的内容是必须的。
在Window上有多种方法。实际上Linux上也有对应的,时间原因,本文只介绍window上的。
远程桌面技术的实现
基于远程桌面要完成的任务目标,其需要实现以下两个核心功能:
- 音视频的传输,即需要让控制机收到受控机的音频跟视频。
- 控制信令的传输,即鼠标键盘的控制信号等
目前主流的远程桌面技术主要有2种:
- 基于VNC(Virtual Network Computing)的远程桌面技术
- 基于RDP(Remote Desktop Protocol)的远程桌面技术
VNC
VNC使用远程帧缓冲协议即(RFB, Remote FrameBuffer)来远程控制另一台计算机,将控制机的键盘和鼠标事件传输到被控制机,同时将被控制机的屏幕图像传输到控制机。
基于其技术原理,VNC有以下优点:
- 跨平台,可以在不同的操作系统上运行,VNC技术本身也有多个客户端和服务端的实现版本,如RealVNC、TightVNC、UltraVNC等
- 开源,VNC的源代码及其很多现代衍生品都是在GNU许可证之下发布的
- 轻量级,VNC的客户端和服务端都是非常轻量级的程序,可以在低配置的计算机上运行
但因为VNC本身的设计时间很早,因此在2023年的今天暴露出了很多的时代局限性:
- 因为其基于像素方块的传输原理,就算是采用部分更新传输的方式,在大量像素变化的情况下会消耗大量的带宽。特别是对于现在的高分屏,其传输的数据量会更大。
- VNC在设计之初被用于局域网内使用,因此没有考虑太多的安全性,虽然密码并不以明文发送,但是如果从网络中嗅探出加密密钥和编码之后的密码,也可能成功破解出密码。
RDP
RDP是微软提出的一种专有协议,扩展了T-120系列协议标准,最早专用于Windows系统的终端和服务器之间的远程桌面连接,之后微软也实现了RDP的MacOS客户端,现在也有很多第三方的实现版本实现了其功能的子集,为GNU/Linux做了适配如xrdp。因此,可以说RDP也一定程度上具有跨平台的性质。
相比于VNC,RDP的实现原理还是比较复杂的:
首先,RDP的最底层是TCP,TCP之上是各层的协议和服务。
- TPKT:是TCP之上的ISO传输服务,允许两个组交换TPDU(传输协议数据单元)或PDU(协议数据单元)的信息单元。
- X.224:连接传输协议,主要用于RDP初始连接请求和响应。
- T.125 MCS:多点通信服务,允许RDP通过多个通道进行通信和管理。
RDP的工作原理是通过TPKT实现信息单元的交换,通过X.224建立连接,使用T.125 MCS打开两个通道来完成两个设备之间的来回数据传输。
RDP的特点功能比较丰富,比如:
- 支持共享剪切板。
- 支持多个显示器。
- 支持虚拟化GPU。
- 支持32位彩色和64000个独立的数据传输通道。
- 通过RC4对称加密算法使用128位密钥对数据进行加密。
- 可以在使用远程计算机时参考本地计算机上的文件系统。
- 远程计算机的应用程序可以在本地计算机上运行。
当然,事物都有两面性,RDP拥有这么多强大功能,也有一些难以避免的缺点:
- 网络速度较慢时,远程连接容易出现延迟。
- 两台计算机在不同的网络上时,其配置过程相当复杂。
- 固定使用3389端口监听,可能成为攻击的目标。
- RDP整体上还是受到微软控制,定制性比较差。
WebRTC和远程桌面
远程桌面的核心需求和WebRTC的核心功能完美契合。
- WebRTC基于ICE/STUN/TURN的NAT穿透方案可以很方便地解决不同网络情况下主机连接的问题,
- WebRTC基于SRTP的传输方式天然提供了实时特征、端到端的加密的数据传输服务。
- WebRTC针对各种网络情况做了音视频传输的大量优化,可以保证各种网络条件下的可用性。
- WebRTC本身其实是Chromium浏览器的一部分,天然具备跨平台的性质。
- WebRTC完全开源,定制性极强,不少公司都基于WebRTC来做自家的直播、云游戏业务。
整体上来讲,WebRTC的优势使其很适合用于远程桌面业务,当然,目前市面上已经有App基于WebRTC实现了远程桌面的功能,比如Chrome Remote Desktop和ToDesk。前者可以理解为是Google用自己WebRTC推出的远程桌面服务,体验了一下,整体上功能比较少,但是连接比较稳定,不过受GFW影响,这玩意在国内应该是处于没法用的状态;后者则是国产远程桌面软件,目前已经比较成熟,提供了企业版、个人版、专业版和游戏版四个版本,从其官网上提供的信息来看,应该是做出了一定成绩。
从技术上讲,基于WebRTC开发远程桌面应用相当合理,开源可控,还有谷歌背书,WebRTC本身在不停地与时俱进,作为上层应用开发的远程桌面也可以及时享受到WebRTC带来的改进。
从业务上讲,WebRTC本身具有的功能可以解决上面所说的VNC和RDP的诸多问题,不过就功能的丰富性而言,可能跟微软的RDP还差一些,但是WebRTC基于音视频的解决方案本身可以优化的上限还是挺高的,毕竟随着人们需求的上升,高分辨率、高帧率也会成为未来远程桌面应用必不可少的功能需求。
WebRTC中的幾種桌面共享方式
一. GDI:第一代桌面采集
Windows 图形设备接口(GDI)是为与设备无关的图形设计的。基于 Windows 的应用程序不能直接访问图形硬件,应用程序通过 GDI 来与设备驱动程序进行交互。GDI 截图就是通过屏幕的DC获取到当前屏幕的位图数据。
- 特点:不受windows版本限制,基本兼容各版本的系统;
- 缺点:使用CPU,消耗大量cpu运算时间,若不单采鼠标,则使用gdi采集时,鼠标会出现闪烁,无法实现过滤指定窗口。
webrtc 实现为 ScreenCapturerWinGdi
二. DXGI:高性能桌面采集技术
DXGI(Microsoft DirectX Graphics Infrastructure)是微软提供的一种可以在win8及以上系统使用的图形设备接口。它负责枚举图形适配器、枚举显示模式、选择缓冲区格式、在进程之间(例如,在应用程序和桌面窗口管理器(DWM)之间)共享资源,以及将呈现的帧传给窗口或监视器以供显示。其直接和硬件设备进行交互,具有很高的效率和性能。
- 特点:Win Vista 以后支持,使用GPU直接处理纹理,效率最高;
- 缺点:根据Direct3D 版本不同,存在硬件的支持以及调用特性 的区别,且因为采集需要获取设备的adapter,所以无法采集桌面窗口。
webrtc 实现为 ScreenCapturerWinDirectx
三. Magnification:弯道超车的采集技术
Magnification API 使用于放大屏幕某个区域的 辅助应用技术,初衷是用于协助视力存在问题或者色弱的用户能跟方便的看到桌面内容的api
- 特点:能实现放大缩小颜色转换等操作,能过滤窗口
四. Window Graphics Capturer:新世代采集技术
WGC 全称为 Windows Graphics Capture 是微软目前主推的一个桌面/窗口采集技术,使用 D3D11 库实现。该采集技术最早在Windows 10 18年3月份的更新中提供。WGC 对比放大镜采集(Magnification Capture) 具有更高的性能、更低CPU及GPU消耗。但是在 使用方面比起其他采集方式会更复杂。
- 特点:效率高,拓展屏采集支持高,1080p采集消耗gpu达到个位数;
- 缺点:
- 当Capture Session开始采集后,在刚开始采集的时候可能存在 HRESULT 为S_OK ,但是画面数据为空,因为这个时候采集Engine可能处于启动中状态;
- 当开始采集是,Windows会在采集源(窗口或桌面)区域增加一个黄色边框去标识正在采集的区域,目前无法设置该边框的样式或者去除该边框;
- WGC 使用SetWindowDisplayAffinity 实现窗口过滤,但是设置的窗口必须为当前进程创建的子窗口才能设置成功,否则无法实现过滤。
webrtc实现为WgcCapturerWin
不管是通过开源的DX实现方式,还是web RTC内置的DesktopCapture方式还是你自定义的,原理上大差不差。
三 传递远程服务器的鼠标和键盘信息
第二步远程桌面提供了音视频或者说图像的能力,实际上的交互需要通过键盘鼠标实现。因此我们需要自己定义一种格式,通过一种数据传递方式达到双向的交互。
首先Web和Window的键盘鼠标映射关系可能有不同。
Web上键盘关系映射表12 Web浏览器上键盘代码映射关系表
Windows上键盘关系映射表(部分),完整映射表,请查看13 Windows上完整键盘代码映射表
- 首先你需要在web端收集鼠标键盘时间的信息,整理成json格式(我们用json格式,你可以用自己的)
- 通过WebRTC内置的DataChannel数据管道,从Web端传递到VDI的服务器端,解析Json格式
- 服务器端通过调用OS的API,触发键盘和鼠标事件的响应。
- VDI需要考虑服务器端或者客户端侧的多显示器情况,此时鼠标键盘事情需要考虑多显示器下的操作。包括DPI变化的影响。
以下简单展示我们的数据格式和对应意义
第一部分 键盘类数据
鼠标操作时发送的数据格式,使用json发送数据格式
keyboardInputData 数据定义
type | int | KeyUp 61, KeyDown 60 | 定义的规范 |
keyCode | int | 获取的keycode,有经过特殊处理key | |
repeat | bool | 是否重复 一般为按住不放自动重复的为true | |
shiftKey | bool | 是否按下shift 多个按键时才会判断生效 | |
ctrlKey | bool | 是否按下ctrl 多个按键时才会判断生效 | |
altKey | bool | 是否按下alt 多个按键时才会判断生效 |
如:
{“type”:61,”keyCode”:16,”repeat”:false,”shiftKey”:false,”ctrlKey”:false,”altKey”:false}
第二部分 鼠标数据格式
鼠标操作时发送的数据格式,使用json发送数据格式
如:
{“type”:74,”x”:26959.607741935484,”y”:18603.767741935484,”dx”:0,”dy”:0,”button”:null,”ds”:null}
参考地址:
MouseInputData 数据定义
定义类型 | 数据类型 | 具体解释 |
type | int | MouseEnter: 70, 未确认 MouseLeave: 71, 未确认 MouseDown: 72, MouseUp: 73, MouseMove: 74, MouseWheel: 75, |
x | double | 鼠标x坐标 [ 0,65535 ] // 坐标 (0,0) 映射到显示表面的左上角,(65535,65535) 映射到右下角 |
y | double | 鼠标y坐标 [ 0,65535 ] // 坐标 (0,0) 映射到显示表面的左上角,(65535,65535) 映射到右下角 |
dx | double | 鼠标和上一个事件在水平方向的移动量[-32767,32767] |
dy | double | 鼠标和上一个事件在竖直方向的移动量[-32767,32767] |
button | int | 鼠标按键被按下,如果多个按键被按下就是多个数据发送 注2 |
ds | int16 | 滚轮变化值 注1 |
四 网络自适应下的图像适配
利用WebRTC自带的拥塞控制算法GCC 可以自动根据码率配置或网络实际情况,提供流畅优先还是画质优先的方案。
并且由于客户侧的显示器分辨率和服务器端的分辨率并不会一致,因此需要在这个过程中确定依照哪一边的。
一般的,由于客户端是直接面对用户的,我们以客户端的为基准。
- 提供多种分辨率呈现方式,比如全屏缩放,按照服务器端原比例,或者拉伸,保持客户端分辨率情况等。
- 前端设定分辨率要求,同步给服务器端,双方约定具体的。约定的数据通过数据或指令传递过去。
根据Citrix之类的VDI的方案,在画质,流畅,网络情况下的自适应的反应从早期的JPEG压缩到使用H264的压缩,实际上和使用WebRTC的最终表现情况是类似的。
这是Citrix文档中列出的各种编码器情况和视频展示格式下的FPS和带宽占用情况。
CODEC | Visual Quality | Encoder CPU | Total FPS | MB transfered |
Bitmap JPG/RLE | Medium | 7% | 3693 | 355MB |
H.265 YUV420 | Medium | 2% | 3766 | 180MB |
H.265 | Build To Lossless | 5% | 3796 | 175MB |
Bitmap JPG/RLE | High | 8% | 3633 | 610MB |
H.265 YUV420 | High | 3% | 3780 | 185MB |
Table 2: Bandwidth comparison between Bitmap Codec, H.265 Video codec with BTL off and H
顺便说一下,当使用NVENC做硬件加速的时候,它有一种模式是【无损模式】,可以支持编码器端保证图像原来的精度质量,从而针对某些场合下的需求需要高清,设置完全无损的要求。
五 多用户下的状态隔离
当多个客户端同时访问VDI服务器的时候,大部分的做法是用VDS,虚拟化出来独立的OS空间,或者偷懒一点用1个OS虚拟化出来不同的用户空间。
微软提供了CreateDesktop这样的API,提供多屏幕空间和切换显示的能力, 这种情况下鼠标和键盘还是全局的,进程没有被隔离,同一时间Active状态的也只能是一个桌面环境,并没有隔离
要实现云雀一样的进程间隔离的能力需要自己维持一套Session机制,在目前的QL中有核心内容的实现。基本思路是:
- 进程画面隔离
- 有一个AgentServer在维持多Session的进程指向和Session内画面,键盘鼠标事件,数据传递等内容的维持。
- 虚拟键盘鼠标事件,发送到SessionID对应的进程
核心思路最终结果类似,接近做了一套WindowsServer下的RDPSession。
RA/SD 衍生者AI训练营。发布者:稻草人,转载请注明出处:https://www.shxcj.com/archives/6740