guacamole架构分为三个部分,分别是guacamoleclient、guacamole server、guacamole proxy。
guacamole client由JavaScript实现,一旦它被加载到用户的web浏览器里,它立即连接到guacamoleserver。这二者之间的交互是通过HTTP之上的guacamole 协议完成的。
guacamole server由Java实现,作为web应用部署。它读取guacamole协议的数据,并将之转发到guacd(guacamoleproxy)。
guacamole proxy是原生应用,由C实现,它将guacamole协议的数据翻译成RDP、VNC等协议的数据,并以客户端的身份连接到多个远程桌面服务端。
协议间的翻译由guacd(guacamoleproxy)完成。guacamole server和guacamole client仅仅知道guacamole协议,无需知道使用的是那个具体的远程桌面协议。
guacamole是一个中心化的网关,通过它可以访问多个远程桌面服务端(支持多种远程桌面协议)。它有可扩展的架构,编译定制化开发,还提供了一套基于HTML5进行远程访问的通用API。
guacamole协议是一个远程屏幕绘制和事件传输协议。拥有这两项能力的协议自然而然与远程桌面协议有相同的功能。guacamole协议与远程桌面协议的设计原则不同,guacamole协议本身不实现任何特定桌面系统的远程桌面功能。
guacamole协议是其他现有远程桌面协议的超集。guacamole要支持一个具体的远程桌面协议(比如RDP),只需要实现一个中间层来做两种协议之间的翻译,这与实现一个原生客户端是一样的,只是原生客户端在本地绘制屏幕,这个中间层却将屏幕绘制到远端屏幕。
guacd(guacamoleproxy)就是这个中间层。
guacd是guacamole的核心,guacd通过从web应用接收到的指令,动态地加载客户端插件,通过客户端插件连接到具体的远程桌面服务端。
guacd伴随guacamole一起安装,以守护进程的形式在后台运行,监听来自web应用的TCP连接。它本身仅仅支持guacamole协议,它并不支持任何具体的远程桌面协议(比如RDP),它通过加载的客户端插件来实现对具体的远程桌面协议的支持。客户端插件被加载后,就独立运行。客户端插件对自身和web应用之间的交互有完全的自主控制能力,知道客户端插件终止运行。
guacd与客户端插件都基于libguac库开发,这个库提供了对guacamole协议更方便和更抽象的使用方法。
它是guacamole系统中,直接与用户交互的部分。
它本身不支持任何其他的远程桌面协议,它依赖于guacd,它本身仅仅作为一个web接口以及权限管理层而存在。
guacamole的协议由指令组成。指令与指令之间用分号分隔,指令内部是由逗号分隔的列表。每个指令内部的第一个元素是指令的操作码,指令内部所有后续的元素都是这个指令的参数。(注:这里都应该是英文的逗号,分号,下同)
操作码,参数1,参数2,参数3,…;
指令内部的列表里的每个元素,都有一个十进制的正整数前缀,这个前缀与元素的值通过英文的句号分隔。这个前缀指明表示元素的值的unicode字符的个数,元素是按UTF-8编码的。
长度.值
服务器发往客户端,或者客户端发往服务器的消息,由任意数量的完整指令组成。客户端发往服务端的消息多数是控制指令(用于建立连接、断开连接)和事件指令(鼠标事件、键盘事件)。服务端发往客户端的消息多数是绘制指令(缓存、裁切、绘图),服务端讲客户端看作远端的屏幕。
例如,一个完整有效的用于设置屏幕大小为1024x768的指令如下:
4.size,1.0,4.1024,3.768;
上述指令将被解码为四个元素:“size”,操作码(也可以叫指令编号,用于区别不同的指令),表示这个指令用于设置屏幕大小。“0”,默认的图层序号(绘制图像的时候有多个图层,这里大概是说第0层,也就是最外层)。“1024”,期望的屏幕宽度,以像素点为单位(就是说期望宽为1024个像素点)。“768”,期望的屏幕高度,以像素点为单位。
guacamole的协议格式这样设计是很必要的,因为这样设计使其能以流的形式被传输,而且易于被JavaScript解析。JavaScript确实原生支持类似于XML、JSON这类格式的消息,但是这类格式的消息都不能以流的形式传输。JavaScript在解析这类格式的消息前必须接收到完整的XML或者JSON的包。而guacamole协议的消息,却可以一边接收一边解析。它的指令内每个元素的长度前缀使得解析器不用遍历每个字符就可以完成指令(元素)之间的跳转。
握手的过程是guacamole协议建立连接的过程。以一个“select”指令开始,这个指令从客户端发往服务器端,并告诉服务器加载哪个协议。
6.select,3.vnc;
服务器接收到“select”指令后,会加载对于的客户端组件,并且回复一个“args”指令,这个指令指明了服务器端需要的参数名列表。
4.args,8.hostname,4.port,8.password,13.swap-red-blue,9.read-only;
客户端接收到服务端可接受的参数名列表后,需要回复给服务器,自己支持的audio、video、image的mimetype,最佳的屏幕尺寸以及分辨率,以及所有的服务器要求的参数的值,即使是空,也要回复。任意一个要求没被满足,连接都将被关闭。客户端回复给服务端的消息如下。
4.size,4.1024,3.768,2.96;
5.audio,9.audio/ogg;
5.video;
5.image,9.image/png,10.image/jpeg;
7.connect,9.localhost,4.5900,0.,0.,0.;(这里就是上一条服务器消息里,服务器要求的参数,客户端把值填上,没有值的,留空白,长度前缀为0,回复给服务器端)
为了表示的易读性,我们将每个指令放在单独的一行,但是在真实的协议中,是不换行的,就是说,指令与指令之间没有换行符。事实上,如果一个指令后接的不是一个新指令的开始,连接将被关闭。
这个例子中,客户端表明了,最佳屏幕尺寸是1024x768。DPI为96DPI。它支持OggVorbis(一种音频压缩格式)的音频(audio),但是不支持视频(video),可以接受PNG、JPEG格式的图片。客户端想连接到localhost的5900端口。余下的三个参数留空。
一旦客户端把这些指令发到了服务器端。服务器端就会尝试以接收到的参数初始化连接(这里初始化的连接应该是guacamole到最终的vnc server或者rdp server的连接),如果连接建立成功,则回复给客户端一个“ready”指令。这个指令包含了新的客户端连接的ID(这里是客户端到服务器端的ID,还是服务器端到vnc server、rdp server的连接的ID,尚不清楚),也标志着交互阶段的开始。这个ID是一个任意的字符串,但是在所有已激活的连接中是唯一的。
5.ready,37.$260d01da-779b-4ee9-afc1-c16bae885cc7;
“ready”指令一发送,真正的交互阶段就开始了。绘制和事件指令在客户端和服务器端传输直到连接关闭。
guacamole协议可以嵌套传送自身。这样大尺寸的指令或者单独的流上的多条指令可以通过同一个流来发送,而且不会互相阻塞。
一个“nest”指令只有两个参数:一个参数是一个任意的整数作为流编号,表明这个数据属于哪个流。另一个参数是指令数据本身。这个流编号用于确定如何在接收到指令数据后重组指令,所以流编号参数很重要。客户端接收到“nest”指令后,流编号相同的,将被客户端按接收的顺序重组为完整的指令,重组一旦完成,指令将会立即被执行。
上述机制很重要,尤其是当数据需要以流的形式而不是单个原子指令的形式发送到客户端的时候。
上图表示了图层运算的概念,包括clear、over、in、out、atop、xor。
合成
guacamole协议通过“通道掩码”提供了合成操作。术语“通道掩码”描述了一种机制,这种机制是通过四种不同的图像数据源来列举所有可能的合成操作。
将这种机制设计在guacamole协议中用于实现所有的图像合成操作。四种不同的图像数据源分别是:源图层在目标图层的不透明区,源图层在目标图层的透明区,目标图层在源图层的不透明区,目标图层在源图层的透明区。对每个“通道”指定一个二进制的值,就会为每种可能的操作创建一个唯一的整数ID。这些操作与Porter和Duff的论文里的操作相似。HTML5里的canvas标签也使用了Porter/Duff方式来描述它的图像合成操作(与其他的图像API一样)。guacamole协议支持的图像合成操作与浏览器已经普遍支持的图像操作一致(尚有少许不支持)。下列合成操作已经被实现,且能够正确地在浏览器里运行。
B out A(0x02)
清除目标图层中源图层不透明的部分,而且不会绘制任何东西。可用于遮盖。
A atop B(0x06)
在目标图层中填充源图层中不透明的部分。
A xor B(0x0A)
与逻辑上的xor操作一样。但是这是图像合成操作,不是位运算,实质含义是在目标图层的透明部分绘制源图层,在源图层的透明部分绘制目标图层。
B over A(0x0B)
与你期望的常规绘制相反。源图层出现在目标图层透明的地方。如同你将目标图层绘制在源图层上,而不是反过来。
A over B(0x0E)
最常见的合成操作,在目标图层绘制全部的源图层,除了源图层透明的地方。
A + B(0x0F)
将源图层与目标图层相加起来,并将结果填充到空白的画布上。
下列操作已经实现,但是可能不能保证在webkit内核的浏览器里正确地执行。这些浏览器总会在源图层的透明部分包含目标图层。
B in A (0x01)
在源图层不透明的地方绘制目标图层,清空所有的源图层透明以及目标图层透明的地方。
A in B (0x04)
在目标图层不透明的地方绘制源图层,清空所有的源图层透明以及目标图层透明的地方。
A out B (0x08)
在目标图层透明的地方画源图层,清空所有的源图层不透明以及目标图层不透明的地方。
B atop A (0x09)
在源图层不透明的地方填充目标图层。
A (0x0C)
填充源图层,忽略目标图层。
下列操作已经定义,但是尚未实现,且在HTML5的canvas里也不存在这些操作。
CLear(0x00)
在目标图层中清空所有已经存在的图像数据。
B (0x03)
什么也不做。
A xnor B(0x05)
将目标图层与源图层不透明的地方相加起来。清空所有目标图层与源图层透明的地方。这与A+B相似,除了透明的地方要清空以外。
(A+B)atop B(0x07)
在目标图层不透明的地方绘制源图层,并保留目标图层余下的地方。
(A+B)atop A(0x0D)
在源图层不透明的地方绘制目标图层,并且复制源图层余下的地方。
图像数据
guacamole协议与其他远程桌面协议一样,提供了一种方法来发送任意的矩形图像数据,并且把它放入缓存或者屏幕上可见的矩形部分。guacamole协议通过“img”指令建立一个流,用于以PNG,JPEG或者WebP的格式传送的原始图像数据。根据使用的格式的不同,通过这种方式传送的图像更新数据可能以RGB或者RGBA(A代表透明度)编码,如果通过libguac传送的话,还会被自动调色。guacamole里以流的方式传输图像的机制,也被用于传输其他类型的数据,比如音频或者文件。关于guacamole协议里流的更多细节,参考“流与对象”章节。
图像数据可以被发送到任意指定的矩形,包括图层或者缓存。将图像数据发送到图层意味着立刻可见,讲图像数据发送到缓存意味着可以在将来被重用。
在图层之间拷贝图像数据
图像数据可以在图层或者缓存之间拷贝,这在屏幕滚动的时候很有用(屏幕滚动的时候,更新的图像经常与之前的图像完全一样),在缓存某个部分的图像的时候,也很有用。
VNC和RDP都支持拷贝屏幕某个区域的图像数据,并将之放置到这个屏幕内其他区域去。RDP还提供了额外的将图像拷贝到缓存和从缓存中提取图像放到屏幕的支持。guacamole吸收了这些概念,并将之进一步发展,将屏幕可见(图层)与屏幕不可见(缓存)的存储统一起来。guacamole的“copy”指令使你可以拷贝一个矩形的图像数据,并且可以将其放置到其他图层。无论这个图层是源图层还是其他的屏幕可见图层,亦或是一个屏幕不可见的缓存。
图像原语
guacamole协议提供的基本图像操作,与Cario或者HTML5的canvas标签提供的图像操作类似。在许多情况下,这些图像操作原语有助于远程绘制,而且传送图像操作原语比直接传送相应PNG图像消耗的带宽小得多,所以也是更可取的方式。但是需要注意的是,过度使用原语会导致客户端的处理负担增加,这可能会导致客户端的性能降低,尤其是客户端的计算能力有限的情况下,比如客户端是手机或者平板。
缓存和图层。
guacamole里的每个绘制操作都会作用到一个具体的图层,每个图层都有一个唯一的编号来标识它自身。当这个编号是负数的时候,这个图层是不可见的,可以用于存储或者缓存图像数据。此时,图层通过编码被引用,且等同于文档中所谓的“缓存”,当通过某个指令引用图层的时候,图层会被自动创建。
有一个图层会被当作默认图层。这个图层的编号为0,调整这个图层大小的时候就会调整整个远端屏幕大小。其他图层创建时候的初始大小与默认图层的大小一致。缓存的初始大小为0x0,并且会自动调整大小来适配装入的内容。
非缓存图层可以在其他图层中被移动或者嵌套。通过这种方式,提供了一种简单的硬件加速合成图像的方式。如果你需要一个窗口浮现在另外的窗口之上,或者你想要移除一些对象,又或者你想要自动保存一些对象之下的图像数据,图层是实现这些需求较好的方式。如果一个图层嵌套在其他图层里,它的位置是相对于父图层的。当父图层被移动或者重排序(调整图层之间的顺序)的时候,子图层会随之移动以及重排序。如果子图层超出了父图层的边界,字图层将被裁切。
guacamole支持传输剪切板内容,音频内容,视频内容和图像数据,以及文件和任意的命名管道。
特殊语义的指令将会通过新分配的流传送。例如,用于播放媒体文件的“audio”或“video”指令。用于传输文件的“file”指令,用于在客户端和服务端传输任意数据的“pipe”指令。在某些情况下,将通过已命名的流传送的结构化集合对象的方式来显示指明流的能力范围和语义。
流一旦被创建,将通过“blob”指令一块一块地传送数据,通过“ack”来确认已收到的消息,流的结束通过一个“end”指令来标识。
当任意一端有改变时,比如键盘上的键被按下,鼠标被移动,剪切板的数据发生了改变,一个描述事件的指令将被发送。
服务端和客户端都可以在任意的时刻断开连接。没有要求在断开连接之前客户端和服务端要协商一下。任意一端想要断开连接,并且原因已知,它们就可以用“disconnect”或者“error”指令来断开连接。
断开连接的时候,“disconnect”指令应当由客户端发送,这很大程度上是出于礼貌,因为“disconnect”指令或许不是总能被及时发送。(这里不明白原文意思)
如果客户端发生了错误,或者服务端检测到客户端插件的出了问题,服务端发送一个“error”指令,在参数中描述错误原因,以此通知客户端连接已经关闭。
libguac是一个C语言的API,用于扩展或者二次开发guacamole。guacamole所有的本地组件都是基于libguac开发的。这个库提供了基础功能用于扩展本地组件的本地功能(通过实现客户端插件的方式)。
libguac主要用于开发客户端插件,比如libguac-client-vnc或者libguac-client-rdp,或者用于开发一个支持guacamole协议的代理,例如guacd。这一章节主要介绍如何使用libguac,以及如何使用它以guacamole协议与协议的对端沟通。
libguac里大多数的函数通过返回一个零或者非零值的方式来处理错误,由当前函数自己选择合适的值。如果发生错误,guac_error变量将被设置为一个对应的值,同时guac_error_message 包含一个静态分配的易于人类阅读的字符串用于描述错误的内容。这些变量刻意模仿errno和errno.h里提供的功能。
guac_error和guac_error_message都是在error.h里定义的。可以通过guac_status_string()方法将guac_error表示的错误信息恢复成一个易于人类阅读的字符串,这个字符串也是静态分配的。
如果客户端插件里的函数在发生错误的时候适当地设置了guac_error和guac_error_message,并通过guacd将这些信息写到系统日志中,将更有助于最终用户和开发者。
上图表示libguac在guacd中的位置,以及客户端插件与guacd之间的关系。
客户端插件是一个通过libdl函数动态加载的库。每个客户端插件都要遵守如下的命名约定,库的名称应当叫做libguac-client-协议。如果不遵守这个约定,guacd在加载客户端插件的时候,将不能找到对应的库(也就是客户端插件)。
当加载客户端插件的时候,guacd以想要加载的客户端插件对应的协议名称来调用guac_client_plugin_open()方法。该方法成功执行后,将以guac_client_plugin实例的形式返回一个句柄给包含客户端插件的库。该实例最终将在guacd结束使用它的时候被guac_client_plugin_close()回收。这些方法既提供给guacd使用,也可以提供给其他的代理实现使用,即使这代理实现驻留在其他客户端插件内(不是很明白原文意思)。
一旦客户端插件加载成功,guacd调用guac_client_plugin_init_client()以初始化客户端,这个方法调用在客户端内部调用guac_client_init(),因而所有的客户端插件内都必须定义该方法(guac_client_init())。这个方法是所有的客户端插件的入口方法,类似于C语言程序中的main()方法。
当guacd按照guacamole协议进行握手操作的时候,它会去读取一个静态分配的,以NULL为截断标志的参数名集合变量,这个变量在客户端插件里声明为GUAC_CLIENT_ARGS。就像guac_client_init()一样,所有的客户端插件如果想要正常工作都必须定义这个变量。握手过程结束后,guacd将会初始化并且填充guac_client数据结构,以及这个数据结构里包含的guac_client_info结构,连同从当前连接的客户端收到的参数数量、参数值一起传送给guac_client_init()方法。
客户端插件需要向guac_client数据结构里填充适当的事件处理函数,一旦这个动作完成,guacd_client_init()函数将成功返回,与客户端的沟通将正式开始,guacd将在需要的时候调用guac_client数据结构里事件处理函数来处理接收到的任意指令。
处理绘制指令时,主要是操作图层(layer)对象,在libguac中,以guac_layer数据结构来代表图层对象。每个guac_layer都由guac_client_alloc_layer()或者guac_client_alloc_buffer()方法来分配。具体使用哪个取决于想要图层还是缓存。图层和缓存使用完后,通过guac_client_free_layer() 或者guac_client_free_buffer()方法来释放分配的资源。
重要:
图层和缓存在分配和释放资源的时候,需要小心地按照类型(是图层还是缓存)调用相应的方法。guac_client_free_layer()只能用于释放guac_client_alloc_layer()方法分配的图层。uac_client_free_buffer()只能用于释放guac_client_alloc_buffer()方法分配的缓存。并且都必须在同一个guac_client实例上调用这些方法。
如果不遵守这些约束,调用这些方法的结果将不可预知。
在远程客户端上创建新的图层是代价高昂的操作,复用已经存在的图层将有助于节约客户端资源,使用上述这些图层管理方法,将使客户端在图层的初始使命完成后能够复用图层。
通过图层(layer)和缓存(buffer),guacamole得以提供硬件加速合成图像以及缓存更新(cached updates)。创造性地使用图层和缓存可以高效地更新客户端,这也意味着流畅的画面以及更好的实时响应。
无论开发者是否分配了新的图层或缓存,总会存在一个默认图层,在libguac中以GUAC_DEFAULT_LAYER表示。如果期望仅在当前连接的远程桌面的主屏幕上绘制,那么绘制指令将默认图层作为操作对象即可。
guacamole里所有的绘制操作都是通过基于guacamole协议向已连接的客户端发送指令的方式来实现的。音频操作,视频操作,文件操作都是一样的。guacamole支持的所有功能最终都将映射到一个或多个指令上,这些指令都是guacamole协议的文档的一部分。
libguac的绘制操作多是凭借Cairo的方法实现的,Cairo的方法在cairo_surface_t对象上进行绘制(参考CairoAPI文档),绘制完成后,通过img指令以及随后的blob指令以流的形式发送到客户端,发送操作通过调研guac_client_stream_png()方法完成的。Cairo是libguac的依赖库,它是一种成熟且稳定的图像缓存绘制库。绘制好的图像最终以易于处理的PNG格式被发送到客户端。
与Cairo API和HTML5 的canvas标签提供的绘图原语(就是直接的绘图指令,比如画一个圆,画一个矩形之类的,而不是画一个image)支持类似,guacamole协议也支持绘图原语。相关的指令在guacamole协议文档中有专门的一章来说明。这些指令与guacamole协议中其他指令一样,在libguac中都提供了对应的方法,其命名符合约定:guac_protocol_send _OPCODE(),这里的OPCODE就是每个具体的指令名称。
每个协议方法都需要一个guac_socket作为参数,这个参数是供libguac使用的带缓冲区的I/O对象。与已连接的客户端对象对应的guac_socket对象存储在当前正在使用的guac_client对象的socket成员中,使用方法如下:
guac_protocol_send_size(client->socket,GUAC_DEFAULT_LAYER, 1024, 768);
有的时候单个指令的尺寸很大,尤其是与音频视频相关的指令,发送这类指令最好的方式就是将其拆分为小的独立的包,并通过“nest”指令来发送这些包。这样,大尺寸的指令就可以与小尺寸的指令间隔着发送,小尺寸的消息就不会被大尺寸的消息阻塞,从而保证了及时响应能力不会降低。由于先前的指令没被解析前,后续的指令不会被解析,所以为了保持较好的交互实时性,应当特别注意不要发送尺寸过大的指令。
libguac提供了guac_socket_nest()方法来自动嵌套发送指令。这个方法会返回一个新的guac_socket,用于传输nest指令的数据,而不是直接在原来的socket上传输nest指令的数据。
注意
guacamole协议要求每个指令的尺寸是明确。如果需要实时传输音频或视频流,必须将其分割成一个个大小确定的数据块,再传输(因为流本身没有明确尺寸)。分隔后的单个块,如果尺寸越小,越容易受网络抖动的影响进而产生明显的延迟。如果尺寸越大,越不容易受网络状况的影响,但是要求客户端等待更长的时间才能开始使用数据(要等待接收完成)。
视频或者音频的数据包的尺寸与nest指令的尺寸是不相关的,即使较大尺寸的数据包也可以使用nest指令传输。而这正是nest指令的目的:将较大尺寸的数据包切分成较小尺寸的数据包再发送。
键盘事件:
当客户端的键盘被按下或者被释放的时候,客户端就会发送一个键盘事件指令到服务端,guacd通过调用客户端插件注册在guac_client变量里的键盘事件处理函数来解析及处理键盘事件指令。传递给这个事件处理函数的参数包括正在改变状态的键的键码以及状态(按下还是释放)。
鼠标事件:
鼠标移动或者键被按下或者释放的时候,客户端发送鼠标事件指令到服务端。这些指令与键盘事件指令一样,通过鼠标事件处理函数被解析和处理。鼠标事件处理函数也是客户端插件注册在guac_client变量中的。传递给鼠标事件的处理函数包括当前鼠标的X和Y坐标,以及一个掩码来表示哪个键被按下哪个键被释放。
GUAC_CLIENT_MOUSE_LEFT:鼠标左键被按下
GUAC_CLIENT_MOUSE_MIDDLE:鼠标中键被按下
GUAC_CLIENT_MOUSE_RIGHT:鼠标右键被按下.
GUAC_CLIENT_MOUSE_UP:鼠标滚轮向上滚动一格
GUAC_CLIENT_MOUSE_DOWN:鼠标滚轮向下滚动一格
剪切板事件:
当客户端发送一个应当被传输到远程桌面的剪切板的数据时,将触发客户端插件注册在guac_client变量里的剪切板事件处理函数。传输的数据是以null作为终止符字符串,以UTF-8格式编码。目前尚不支持其他格式的剪切板数据。
处理来自服务端的消息
guac_client变量中的handle_messages指向服务端消息处理函数。为每个服务端消息创建一个线程来处理它,这种方式看起来直观,但是一定不能这样做。因为guacd的一个重要任务是在自身和guacamole client之间传输同步(sync)指令,来判断guacamole client现在的状态,是在线还是离线。而且这个处理函数会限制服务端消息的频率。如果忽略了这一点,guacamole client将会被同步指令淹没,导致处理不过来,使用户体验不佳。
这是一个Java API。它提供了一个基础方法用于在guacamole client与guacd之间传输guacamole协议的数据。这个API的主要目的是方便二次开发,以加强其安全模型。
本模块通过GuacamoleHTTPTunnelServlet类来实现http隧道。这个servlet接收所有的来自js客户端的请求,并翻译为connect、read、write三种请求,并分别调用对应的处理方法:doConnect()、doRead()、doWrite();
使用这个API时,只需要继承GuacamoleHTTPTunnelServlet类,并且实现自己的doConnect()方法。doConnect()里是一个很适合做授权的地方,这里授权失败,将不会创建http隧道。
GuacamoleTunnel由GuacamoleSocket实现,后者是一个抽象接口用于访问下层的到guacd的TCP socket连接。
该模块提供了对guacamole协议的支持。这里实现了两个类:GuacamoleReader和GuacamoleWriter,这两个类与Java里的Reader和Writer的类类似,不过专用于guacamole协议的处理。
GuacamoleReader
这个类提供了基本的read()方法,以char数组的形式返回一个或多个完整指令。
GuacamoleWriter
这个类提供了基本的write()方法,用于写入完整的指令(GuacamoleInstruction)。
·这是一个Java Script API
·用途:
用于开发guacamole客户端
·提供:
①客户端的Java Script实现
②HTTP隧道:从JavaScript中获取协议数据到guacd或web应用程序的服务器端
③鼠标、触摸、键盘的抽象对象
④可扩展的虚拟键盘
·优点:
①已经实现了guacamole协议的绝大部分指令
②大部分指令由客户端自动处理,
需自己处理:name(用于命名连接)、clipboard(用于在客户端更新剪贴板数据)、error(当服务器端出错时使用)
·使用方法:
①实例化
②手动添加客户端
③Connect(),传数据
①Java script端请求,Java端处理
②通过将相对URL提供给服务器端隧道servlet来创建隧道
var tunnel = new Guacamole.Tunnel(“tunnel”);
·鼠标:
对象:guacamole.mouse
实例化:mouse.state
事件:onmousedown、onmousemove、onmouseup
·键盘:
对象:guacamole.keyboard
事件:keyup、keydown
处理方法:K11 keysym 用来表示具体哪个键被按下
//todo
已经部分实现了触摸事件向鼠标事件的模拟(down、up、move),包括移动和点击鼠标(绝对定位模式和相对定位模式),未实现触摸的滑动操作(与鼠标滚轮事件的区别,滑动有任意方向,滚轮只有前后两个方向。)
对象:guacamole.touchpad/ guacamole.touchscreen
实例化:mouse.state
·键盘布局
Layout XML :
·键盘显示
①添加一个键盘
②设置像素,键盘的宽与高
·设计样式
有几个CSS类,可用来自行调整
例如:guac-keyboard
、
guac-keyboard-gap
、
guac-keyboard-cap等
·事件的处理
与实体键盘生成的
event完全相同
只含有
keyup
、
keydown
因为都只包含
X11 keysym
//todo
摄像头
声卡
usb
Web应用程序通过收集可用的所有凭据并将它们传递到类“authentication providers”来处理此身份验证。 发送完数据,返回上指定对象,该对象提供对其他用户和连接(如果有的话)的受限访问。
最简单的Guacamole认证扩展需要pom.xml文件、.java文件、guac-manifest.json文件。为简单起见,使用Maven创建扩展。
首先,需要三个文件:
pom.xml文件列出guacamole-ext作为dependency
.java文件实现认证提供程序的功能
guac-manifest.json文件描述扩展并指向类“authenticationproviders”。
简单实例创建的文件清单:
pom.xml
TutorialAuthenticationProvider.java
(位置src/main/java/org/glyptodon/guacamole/auth)
guac-manifest.json(位置src/main/resources)
上述三个文件准备好,授权扩展就可以创建,可以安装到Guacamole。创建授权扩展会生成target/guacamole-auth-tutorial-0.9.9.jar。
当我们接收到认证消息,需要guacamole.properties中的相关属性进行验证。用户名和密码匹配时,授权扩展会读取配置信息并存为GuacamoleConfiguration,返回配置信息,告诉Guacamole用户已经通过认证。
简单实例的属性,定义在TutorialProperties.java:
tutorial-user
tutorial-password
tutorial-protocol
tutorial-parameters
从guacamole.properties文件读取数据,如果认证通过,根据数据进行配置。
把需要的属性添加到guacamole.properties,将生成的target/guacamole-auth-tutorial-0.9.9.jar复制到GUACAMOLE_HOME/extensions,重启Tomcat。