今天折腾了一整天 Guacamole,遇到了臭名昭著的坑,且听我一一道来。
简单来说 Guacamole 提供了浏览器端访问的桌面系统的解决方案。Guacamole 提供的解决方案主要由两部分组成:
- 浏览器端基于 HTML5,Canvas 技术: Guacamole Client 的 Guacamole-Common-JS 组件
- Guacamole Client 的 Guacamole Web 组件,
- Guacamole Server 仍然分为两个部分:
- Guacamole Web 服务容器
guacd
守护进程与RDP
/VNC
/TELNET
等其他服务进行通信。
下面这张图很好的解释了 guacamole
的架构,出自官网手册:
部署 Guacamole Server
部署 Guacamole 分两步:
- 部署 guacamole-server
- 部署 guacamole-web-service
部署 guacamole-server
|
|
部署 guacamole-client:
|
|
再在 /etc/guacamole/
下创建 user-mapping.xml
:
<user-mapping><authorize username="changkun" password="123"><protocol>vnc</protocol><param name="hostname">localhost</param><param name="port">5900</param><param name="password">VNCPASS</param></authorize></user-mapping>
最后启动 guacamole
/tomcat7
/vnc
服务即可:
|
|
关闭 vnc 服务用
vnc4server -kill :1
。
这时便可以在 8080 端口访问 guacamole
服务了。
Guacamole 对 UTF-8 的支持
关于 UTF-8 和 Unicode 之间的区别,简单来说 Unicode 是一种规范标准,规定了字符集的编码;而 UTF-8 是 Unicode 的一个具体的实现,解决了 Unicode 的存储问题。
关于 UTF-8 的具体实现细节可以归为两点:
- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
- 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
这里说的比较精炼,可以参考阮一峰的一篇关于字符编码的文章。
Guacamole Client 对 UTF-8 的编码
对于剪切板之间的传输不支持中文的情况,自然最先想到从客户端查起。首先查到官方文档中对于剪贴板的描述,实现剪贴板之间的传输主要依赖 setClipboard
方法和 onclipboard
事件。
对于 setClipboard
来说:
|
|
看到这里发现对于字符的处理每次传输4096长度的字符串,知道传输完成才关闭写入流。所以关于编码的部分取决于 Guacamole.StringWriter
对于 .sendText()
的实现。
在这个实现中,.sendText()
会将字符串先编码为 UTF-8
然后通过 .sendData
方法传输:
|
|
那么问题就落在了关于对 UTF-8 的编码上了,下面是 guacamole-client
关于 UTF-8 编码的关键实现:
|
|
总的来说 __encode_utf8()
这个方法最终实现了对 UTF-8 字符串的编码转换,上面给出的例子中,『你好』这两个汉字最终被编码为 3-bytes
的 UTF-8
,分别是 E4 BD A0
和 E5 A5 BD
。
看起来客户端这边没什么问题,那么我们再来查一查服务端的代码。
Guacamole Server 对 UTF-8 的解码
先来看对 UTF-8 字符的处理是怎么实现的,由于 guacd
底层由 C
语言实现,就不再粘贴代码了,我们可以单独把 /src/libguac/unicode.c
和 /src/libguac/guacamole/unicode.h
这两个文件单独拿出来,注释掉 unicode.c
里面的 #include "config.h"
,然后编写下面的 main.cpp
:
|
|
我们把三个文件一起编译 gcc main.c unicode.c
,容易发现最后输出的确实是『你好』两个汉字,那么,究竟为什么最后还是没办法传递中文?
guacd
与 VNC 交互
首先我们需要定位到src/common/guac_clipboard.c:
这个函数用于设置剪切板所粘贴文字的类型:
|
|
说明 guacd
在处理剪切板本身是没有问题的。
那么它在 VNC
上究竟是怎么处理剪切板的呢?/src/protocols/vnc/clipboard.c
揭示了一切:
|
|
"text/plain"
看起来完全没有问题,Excuse me????????,问题在哪儿?最后,终于查到了大坑原来在 VNC 协议本身身上。
VNC 的大坑
最后的最后,我们终于把坑锁定在了 VNC
这个协议本身上,我们能够查到 RFP, Remote Framebuffer Protocol 这个协议本身的描述,在 7.5.6 ClientCutText
和 7.6.4 ServerCutText
中:
7.5.6. ClientCutText
RFB provides limited support for synchronizing the “cut buffer” of selected text between client and server. This message tells the server that the client has new ISO 8859-1 (Latin-1) text in its cut buffer. Ends of lines are represented by the newline character (hex 0a) alone. No carriage-return (hex 0d) is used. There is no way to transfer text outside the Latin-1 character set.
|
|
+————–+————–+————–+ | No. of bytes | Type [Value] | Description | +————–+————–+————–+ | 1 | U8 [3] | message-type | | 3 | | padding | | 4 | U32 | length | | length | U8 array | text | +————–+————–+————–+
|
|