并发式集中远程控制系统的设计与实现
2018-06-14罗伟雄时东晓曾纪霞
罗伟雄,时东晓,曾纪霞,刘 岚
(广州城市职业学院网络与教育技术中心,广东 广州 510405)
0 引言
目前众多的计算机实验室管理系统[1-2]都有远程控制[3]功能,但是只能在同一时间对一台客户机进行控制,无法实现并发式的集中控制,因此在软件安装和考前测试时,无法发挥远程控制的优势来减轻管理员的工作量。尤其是当要输入不同的账号等信息时,只能逐台机器操作,工作量大,操作繁琐。
而随着虚拟化技术的普及,虚拟化云桌面[4-6]也逐步进入计算机实验室。但是该技术只能解决系统的安装、部署等问题,对于考前测试等,需要进行操作和输入信息的工作,虚拟化云桌面技术也无能为力。
为此,笔者编写了一套计算机实验室并发式集中远程控制系统以解决上述问题。
1 系统特点
本系统最大的特点是,可以在一台管理机上同时同步对同一子网内的所有客户机进行远程控制,而且还可以让各客户机自动输入不同的字符串,实现诸如测试考试软件时,自动输入不同账号的目的。另外系统还提供了同步各个客户机当前活动窗口位置和大小的功能,以达到鼠标的精准定位。同时系统还可以对客户机进行分组,实现分批控制。除此之外,系统还可以监控客户机的屏幕画面,实现远程关机、重启等常见的操作。
由于当前计算机实验室以Windows系统居多,因此整套系统是基于Windows环境设计和开发的。系统分为客户端和管理端两部分。客户端部署在每台受控的机器上,管理端部署在管理机上,整个系统不需要安装,拷贝即可使用。为了提高系统的兼容性,以适应从Windows XP版本开始的Windows系列系统的使用,同时为了便于部署,避免依赖JAVA、.NET等框架,系统采用Delphi语言进行开发,以全编译方式生成原生可执行代码,进一步提高执行效率。
2 系统原理
系统的实现原理如图1所示。
图1 系统原理Fig.1 Sy stem principle
从图1中可以看到,系统只需一台管理端即可实现对多台客户端的并发式远程控制[7],在客户端群中,需要一台客户机作为种子机。由于计算机实验室的机器其操作系统基本上都是通过克隆来批量安装的[8],因此不论是屏幕分辨率或者是桌面图标的位置,还是开始菜单上各菜单项的顺序都是相同的,而且每次打开相同的窗口其位置和大小也是相同的。因此,只需观察任意一台客户机就可以知道其他客户机的状态。
首先,在客户端群中任意选取一台客户机作为种子机,然后向种子机发送获取屏幕截图指令(图1中功能 1.1),种子机收到该指令后返回当前屏幕截图给管理端(图1中功能1.2),管理端收到后,显示种子机屏幕画面;当需要同步当前活动窗口状态时,管理端向种子机发送获取当前活动窗口状态指令(图1中功能2.1),种子机收到指令后获取当前活动窗口状态并返回给管理端(图1中功能2.2),管理端收到返回信息后向客户端群发送窗口状态同步指令(图1中功能2.3),各客户端收到指令后执行窗口同步操作;在进行鼠标、键盘和其他指令的集中操作时,管理端直接向客户端群发送相应的指令。
向种子机发送指令时,采用单播方式,而向客户端群发送指令时,由于无需等待客户端返回信息,而且指令又比较短,不需要分块发送,加上客户端比较多,因此适合采用广播方式进行传输。
UDP广播[9],只需要把信息发送到广播地址255.255.255.255上,则同一广播网络上的所有主机都会收到该信息,从而有效提高了发送效率,减少了对网络资源的占用。
而TCP协议由于是面向连接的,在发送数据前必须与对方建立连接。如果采用TCP协议,则管理端在发送数据前,必须与每个客户端建立TCP连接,这样势必会消耗大量的时间和资源。另外对于计算机实验室这样的局域网,一般网络通讯质量比较高,干扰比较少,使用UDP系统即可满足需求,因此系统采用UDP广播方式作为指令的传输协议。
而对于种子机屏幕画面数据的传输,由于其数据量比较大,因此需要分块传送,这就要求传输过程中必须要有流量控制、差错控制和拥塞控制[10]。因此系统选择TCP作为其传输协议,这样既可以保证系统的可靠性和稳定性,同时也降低了系统开发的复杂度,不需要编写额外的流量控制、差错控制和拥塞控制等代码。
3 系统功能设计与实现
3.1 指令的设计和发送
系统指令的基本格式为:<命令> [参数 1|参数2|…|参数 n]。
命令和第一个参数之间用空格间隔,各参数之间用“|”间隔。
考虑到命令的数量有限,因此规定命令为3个字符的固定长度。例如,获取窗口状态指令为WIN,键盘指令为CMD等。
各参数之间用“|”间隔的原因是由于在设置窗口状态时,系统是根据窗口标题来查找窗口的,而窗口的标题是允许包含空格、逗号、分号等常见的分隔符。因此如果使用这些常见的符号作为分隔符就会导致冲突。考虑到窗口标题一般是以文件名来命名的,因此可以使用Windows系统不允许包含在文件名中的字符作为参数的分隔符,如此即可解决窗口标题和命令参数之间的冲突问题。所以本系统采用了“|”作为参数的分隔符。
对客户端群发送指令,前文已阐述,采用UDP广播的方式,但是在特殊的情况下则不能使用广播传输。例如对某一台或几台客户机进行控制。此时可以采用单播或组播的方式。实际使用中,笔者发现,有些系统瞬时承受能力有限,例如当50个用户同时登录时,会出现拥塞现象,导致部分用户出错,需要重新登录。此时只能让各客户端分批登录,而每批的数量设置多少合适,又取决于网络带宽、服务器的配置等因数,在实际操作中不好把握。为次,笔者采用UDP单播方式,向受控端间隔发送命令的方法来解决。也就是每次以单播方式向一个客户端发送指令,然后暂停若干时间,再向下一个客户端发送指令,其暂停时长可以由用户自行设定。这样就可以很好地解决系统瞬时承受能力的问题,同时也不会增加操作员额外的工作量。另外利用单播传输还实现了分组控制的功能,只需任意选择若干台客户机,然后使用单播方式发送,即可实现分组控制的效果,操作方便简单。
发送指令的核心代码如下所示。
if isBroadcast then //广播方式发送
SendString(UDPClient, '255.255.255.255',Port, Cmd)
else //单播方式发送
for I := 1 to HostList.Items.Count do
begin
SendString(UDPClient, HostList.Items[I - 1],Port, Cmd);
if isInterval then //间隔发送
Sleep(cnInterval); //暂停若干时间
end;
系统首先判断是否为广播方式,若是,则调用SendString函数向客户端群发送指令,其中参数255.255.255.255为广播地址,Cmd为命令字符串;否则,采用单播方式,此时循环调用SendString函数向选定的客户端逐个发送指令,其中 HostList.Items[I - 1]参数为所选定的客户端的IP地址。另外,如果是间隔发送,则发送一条指令后调用 Sleep函数暂停若干时间。
3.2 获取客户端屏幕画面
由于管理端给种子机发送截屏指令后,需要等待种子机返回数据,而且此过程是不断重复的,另外种子机返回的屏幕截图数据比较大,往往需要分块发送,因此本系统采用TCP作为其传输协议。一旦连接创建后就可以持续的收发数据,而且TCP协议提供了流量控制、差错控制和拥塞控制,保证了数据的可靠传输,极大地降低了系统开发的复杂度。
系统流程为,建立TCP连接后,管理端向种子机发送截屏指令,然后等待种子机返回屏幕画面数据,接收到数据后显示客户端屏幕画面,然后再次发送截屏命令,如此重复,截至系统关闭客户端屏幕窗口为止。其核心代码如下所示。
while repeatNow do
begin
try
//发送截屏指令
IdTCPClient.WriteLn(cnPIC);
//读取客户端返回的数据
IdTCPClient.ReadStream(ScreenStream);
//解压流数据
UnCompressionStream(ScreenStream);
//显示客户端屏幕
ShowImage;
//响应外界事件
Application.ProcessMessages;except
//出错时显示错误信息并退出循环
ON E:Exception do
begin
ShowErrorMessage(E.Message);
repeatNow := False;
end;
end;
end;
这里截屏指令不需要附带任何参数,因此直接发送截屏指令即可。如前所述,由于TCP协议已经提供了流量控制、差错控制和拥塞控制,因此管理端通过ReadStream函数直接读取流数据,即可获得客户端返回的截屏数据。
另外,为了减少传输的数据量,系统采用了数据压缩技术[11],因此管理端收到的是经过压缩的数据流,所以此处先调用 UnCompressionStream函数解压数据流,然后再显示屏幕画面。
对于客户端,当收到截屏指令后,首先获取Windows桌面图像,然后对数据流进行压缩,接着把数据传送给管理端。其核心代码如下所示。
if Command = cnPIC then
begin
BmpStream.Clear;
//获取Windows桌面图像
CaptureScreen(0, 0, Screen.Width, Screen.Height);
//压缩数据流
CompressionStream(BmpStream);
AThread.Connection.OpenWriteBuffer;
//发送数据
AThread.Connection.WriteStream(BmpStream,True, True, BmpStream.Size);
AThread.Connection.CloseWriteBuffer;
end
其中方法 CaptureScreen是获取 Windows桌面图像,其主要过程是通过调用Windows的API函数GetDC获取Windows桌面句柄,然后复制其画布。CaptureScreen方法的核心代码如下所示。
Bitmap := TBitmap.Create;
//设置像素格式
Bitmap.PixelFormat := PixelFormat;
JPG := TJPEGImage.Create;
JPG.PixelFormat := jf8Bit;
//设置压缩比
JPG.CompressionQuality := CompressionQuality;
JPG.Compress;
Desk := TCanvas.Create;
//获取Windows屏幕句柄
Desk.Handle := GetDC(hwnd_desktop);
with Bitmap do
begin
Width := Screen.Width;
Height := Screen.Height;
//拷贝画布
Canvas.CopyRect(Canvas.cliprect, Desk, Desk.cliprect);
end;
//保存为JPG格式
JPG.Assign(Bitmap);
//写入到数据流
JPG.SaveToStream(BmpStream);
BmpStream.Position := 0;
为了进一步减少图像的数据量而又不至于较大地降低图像的质量[12],系统使用了 JPEG格式来存储截屏画面,同时还允许用户动态调节图像的像素格式和压缩比,以达到用较少的数据量传输较高图像质量的目的。
3.3 同步窗口状态
为了防止因窗口位置和大小不相同而导致鼠标点击时的位置错误,可以在需要时先同步窗口的位置和大小。
管理端先向种子机发送获取窗口状态命令,其核心代码为:
SendCommand(ClientIP, Port, cnWIN)
其中ClientIP为种子机的IP地址,cnWIN为命令字符。
当种子机收到命令后即开始获取当前活动窗口的标题、位置和大小等信息,然后返回给管理端。获取窗口状态主要是通过Windows API函数获取当前活动窗口的句柄,然后再调用相应的函数获取窗口的位置、大小和标题等信息,其核心代码如下所示:
Hnd := GetForegroundWindow; //获取当前活动窗口句柄
if Hnd <> null then
begin
//获取窗口区域
GetWindowRect(Hnd, Rec);
WX := Rec.Left;
WY := Rec.Top;
WW := Rec.Right - Rec.Left;
WH := Rec.Bottom - Rec.Top;
GetMem(Buffer, MaxBuf);
//获取窗口标题
if GetWindowText(Hnd, Buffer, MaxBuf) <> 0 then
WCaption := Buffer
else
WCaption := '';
FreeMem(Buffer);
end;
管理端收到种子机返回的窗口状态信息后,即向其他客户机发送窗口同步指令。
其他客户机收到该指令后首先通过窗口标题查找需要同步的窗口,然后设置其位置和大小,其核心代码如下所示:
Hnd := FindWindow(nil, PCHAR(pCaption)); //根据标题查找窗口
if (Hnd <> 0) then begin
//设置为活动窗口
SetActiveWindow(Hnd);
BringWindowToTop(Hnd);
ShowWindow(Hnd, SW_SHOWNA);
SetForegroundWindow(Hnd);
//设置位置和大小
SetWindowPos(Hnd, HWND_TOP, pX, pY,pW, pH, SWP_SHOWWINDOW);
end;
3.4 鼠标指令
鼠标的操作包括位置移动、按键的按下和按键的弹起,它们分别通过管理端控制屏幕上的 On-MouseMove、OnMouseDown和OnMouseUp事件来捕获这些信息。
位置的移动。当鼠标在控制屏幕上移动时,管理端随即把鼠标的位置信息发送给客户端;客户端收到指令后,通过 SetCursorPos函数设置鼠标的位置。
鼠标按键按下。当鼠标按键按下后,管理端首先检查是左键还是右键被按下,然后把按键信息发送给客户端;客户端收到指令后,通过Mouse_event函数模拟鼠标的相应按键按下。
鼠标按键弹起。当鼠标按键弹起后,管理端同样先检查弹起的是左键还是右键,然后把该信息发送给客户端;客户端收到指令后,通过Mouse_event函数模拟相应的鼠标按键的弹起。
3.5 键盘指令
键盘的操作包括按键的按下和按键的弹起,它们分别通过管理端控制窗口上的 OnKeyDown和OnKeyUp事件来捕获。
在控制窗口上,当按下键盘的某个按键后,管理端首先获取被按键的键值,然后把键值信息发送给客户端;客户端收到后,通过函数 keybd_event模拟键盘按下相应的按键。
在控制窗口上,当键盘的某个键弹起后,管理端首先获取所弹起键的键值,然后把该信息发送给客户端;客户端收到后,通过keybd_event函数模拟该按键弹起。
对于组合按键的捕获,由于 OnKeyDown和OnKeyUp事件除了能捕获可见字符按键之外,功能键,如Ctrl、Shift等,同样能捕获。例如管理端执行Ctrl+A的键盘操作,其过程是先按下Ctrl键,然后再按下A键,接着弹起A键,最后弹起Ctrl键,因此管理端只需按捕获的顺序将相应的信息发送给客户端即可,无需额外的操作。
3.6 自动输入信息
有时候在进行集中控制时,需要客户端各自输入不同的信息,例如在进行考试系统的测试时,需要输入不同的账号。经过分析,这些数据往往是按顺序排列,都是有规律的。例如下面的账号:AZ1844001,AZ1844002,…,AZ1844090,前面AZ1844是相同的,后面三位为顺序号。此时可以先统一输入前面相同的部分,然后由客户端自动输入后面的顺序号。而顺序号可以由IPv4地址的第四段加上偏移量来计算获得。
由于实验室的计算机其IP地址不一定都从1开始分配,而且所需输入的顺序号也不一定都从1开始,因此就要通过偏移量来进行控制。例如,IP地址从192.168.1.100开始分配,而所需输入的顺序号从51开始,则偏移量为51-100=-49,经计算,该客户端的顺序号为100+(-49)=51,符合要求。
另外,自动输入时还需要规定字符的长度,如上例,则要规定长度为 3,位数不够时通过在前面补0来填充。
客户端在自动输入时,是通过keybd_event函数模拟某个按键被按下接着再弹起来实现的。
4 结论
本系统通过使用集中控制技术,实现了在一台管理机上,对同一子网内的所有客户机进行集中、统一、同步地远程控制,而利用单播和时延技术,解决了某些系统瞬时承受能力有限的问题,同时系统还实现了在不同的客户端自动输入不同字符串的功能。该系统的使用,极大地减少了管理员对计算机实验室在进行软件部署和考前测试等日常维护和管理当中的工作量,有效地提高了工作效率。
目前该系统已开发完成,并已取得国家计算机软件著作权,在笔者所在单位的各计算机实验室得到了持续而广泛的应用,为计算机实验室的维护工作发挥了重要的作用。
[1] 赵康, 李康, 孟晨宇, 初敬敬, 康晓凤. 基于C/S架构的远程协助和管理系统[J]. 软件, 2015, 36(4): 14-17.
[2] 陈剑伟, 李志芳. 混合模式校园机房在线管理系统的设计与实现[J]. 软件, 2017, 38(6): 70-74.
[3] 朱永强, 汤雄. 基于VNC的远程桌面传输协议分析与研究[J]. 计算机系统应用, 2016, 25(11): 284-287.
[4] 陆万青. 虚拟化云桌面在高校计算机实验室中的应用研究[J]. 软件, 2016, 37(12): 230-232.
[5] 宋金城. Hyper-V虚拟环境下高职院校计算机实验室网络设计研究[J]. 软件, 2014, 35(11): 124-125.
[6] 易俗. 基于桌面云的高校计算机实验室管理研究[J]. 辽宁大学学报(自然科学版), 2016, 43(3): 277-280.
[7] 许葵元. 计算机网络远程控制系统的应用研究[J]. 现代工业经济和信息化, 2015, 5(24): 106-107.
[8] 何少宜. 高校计算机实验室管理与维护探究[J]. 无线互联科技, 2017(22): 133-134.
[9] 万玉铸, 徐志江, 卢为党, 华惊宇. 具有高吞吐量的可靠UDP协议[J]. 计算机工程与设计, 2017, 38(12): 3202-3206+3212.
[10] 郭峰. 计算机通信传输控制技术研究[J]. 信息与电脑(理论版), 2017(9): 147-148.
[11] 田浩, 孙剑伟. 基于卫星数据缩减的传输优化技术研究与实现[J]. 软件, 2017, 38(2): 98-104.
[12] 李素钧, 廖胜, 李强. 基于多核DSP的H. 264图像压缩编码的并行化实现[J]. 电子设计工程, 2017, 25(4): 126-129.