当前位置:文档之家› 开源远程控制RealVNC源代码中的通讯协议RFB

开源远程控制RealVNC源代码中的通讯协议RFB

开源远程控制RealVNC源代码中的通讯协议RFB(远程帧缓冲)----- 一分类:远程控制业余研究开源软件2014-05-27 18:04 2040人阅读评论(1) 收藏举报在网上流传的gh0st3.6源代码中,远程桌面总是存在CPU占用率高和画面更新不及时等问题。

于是想到了著名的开源远程控制RealVNC 它采用了远程帧缓存的协议(Remote Frame buffer)在网上找到的一段关于RFB的描述RFB 是真正意义上的“瘦客机”协议。

RFB协议设计的重点在于减少对客户端的硬件需求。

这样客户端就可以运行在许多不同的硬件上,客户机的任务实现上就会尽量的简单。

RFB协议对于客户端是无状态的。

也就是说:如果客户端从服务器端断开,那么如果它重新连接相同的服务器,客户端的状态会被保存。

甚至,一个不同的客户端可以用来连接相同的RFB服务器。

而在新的客户端已经能够获得与前一个客户端相同的用户状态。

因此,用户的应用接口变的非常便捷。

只要合适的网络连接存在,那么用户就可以使用自己的应用程序,并且这些应用会一直保存,即使在不同的接入点也不会变化。

这样无论在哪,系统都会给用户提供一个熟悉、独特的计算环境。

显示协议显示协议是建立在“把像素数据放在一个由x,y定位的方框内”这单一图形基础之上的。

乍一看上去,把这么多的用户接口组件绘制出来是非常低效的方法。

但是,允许不同的像素数据编码方式,使得我们在处理不同的参数(如:网络带宽,客户端的绘制速度,服务器处理速度)有了很大程度的灵活性。

通过矩形的序列来完成帧缓存的更新。

一次更新代表着从一个可用帧缓存状态转换到另一个可用,因此有点和视频的桢类似。

尽管矩形的更新一般是分开的,但是并不是必须的。

显示协议的更新部分是由客户端通过命令驱动的。

也就是说,更新只是在服务器端响应客户端的请求时发生的。

这样就让协议更新质量是可变的。

客户端/网络越慢,更新速度也就越慢。

对于一些应用来说,相同区域的更新是连续不断的。

如果用一个慢的客户端,那么帧缓存的缓存状态是可以被忽略的。

这样也可以减少对客户端网络速度和绘制速度的要求。

输入协议输入协议是基于标准工作站的键盘和鼠标等设备的连接协议。

输入事件就是通过把客户端的输入发送到服务器端。

这些输入事件也可以通过非标准的I /O 设备来综合。

例如,手写笔引擎可能产生一个键盘事件。

像素数据的表示初始的交互涉及到RFB客户端和服务器之间传输像素数据格式和编码方式的协调。

这种协调被设计的让客户端的工作尽量简单。

而设计的底线是:服务器必须按照客户端的要求格式来提供像素数据。

如果客户端可以同样的处理多种数据格式或编码格式,那么一般会选择服务器端易于生成的格式。

像素格式涉及如何通过像素值来实现不同颜色的重现。

最常用的一般像素格式是24 位或16位的“真彩色”,它通过位来直接实现像素值到红、绿、蓝亮度的转换。

8 位“颜色映射”可以任意映射像素值到RGB亮度的转换。

编码指一个矩形的像素数据如何通过网线传输。

每个像素数据的矩形都加上了一个头,给定矩形在屏幕上的X、Y坐标、矩形的宽和高,以及指定的编码类型。

而后数据本身就是采用这种特定的编码方式。

数据本身遵循特定的编码。

目前的编码方式主要有Raw、CopyRect、RRE、Hextile和ZRLE.在实际应用中我们一般使用ZRLE、Hextile和CopyRect,因为它们提供了典型桌面的最好压缩。

其他可能的编码方式还包括,用于静态图片的JPEG和用于动态图像有效传输的MPEG。

协议可以通过增加新的编码方式来进行扩展。

协议扩展协议可以通过以下方式进行扩展:新的编码方式一种新的协议可以通过与现存的客户端和服务端进行相关兼容的添加。

因为现存的服务器将会忽略它们所不支持的新编码方式。

所以客户端通过新的编码方式进行请求也就不会有结果返回。

伪编码方式除了真正的编码方式,客户端也可以请求“伪编码”通告服务器,它支持某一协议的扩展。

服务器如果不支持这种扩展,那么它将忽略。

值得注意的是:客户端必须先假设服务器端不支持这种扩展,直到它获得服务器端支持的确认。

新的安全方式添加一个新型的安全方式会带来无限的灵活性,它通过修改协议的一些行为,但是并没有牺牲现存客户端和服务器端的兼容性。

客户端和服务器端可以通过协议好的安全方式进行交流,当然并不一定与RFB协议类似。

无论如何你都不应使用不同的版本号。

RFB协议的版本是由RealVNC公司来制定的。

如果你使用一个不同的协议版本可能与RFB/VNC不兼容,要保证协议的兼容性,请联系RealVNC公司。

这样会减少在编码方式和安全类型上的冲突。

协议消息RFB协议可以进行可靠的传输,如字节流或基于消息的。

和大多数协议一样,它也是通过TCP /IP协议簇连接。

协议由三步完成连接。

首先是握手报文,目的是对协议版本和加密方式进行协商。

第二步是初始化报文,主要用于客户和服务器的初始化消息。

最后就是正常协议的交互,客户端可以按需发送消息,然后可以获得服务器的回复。

所有的消息以消息类型开始,接下来是特定的消息数据。

协议消息描述的基本类型有:U8、U16、U32、S8、S16、S32。

U表示无符号整数,S表示有符号整数。

所有字节整数(除了像素值本身)遵从big endian顺序。

big endian或者little endian跟cpu有关,从而影响整数在内存中的排列顺序。

big endian是高字节在前,little endian是低字节在前,网络字节序一般是big-endian。

PIXEL代表一个像素值bytesPerPixel字节,8XbytesPerPixel = bits-per-pixelRealVNC 分为客户端和服务端客户端名为 VNCViewer服务端名为 VNC客户端连接到服务器端这时候服务端会给客户端发送当前提供最大支持的版本号。

[cpp]view plaincopyprint?1.//初始化RFB协议2.void SConnection::initialiseProtocol()3.{4. cp.writeVersion(os);5. state_ = RFBSTATE_PROTOCOL_VERSION;6.}7.//写入版本8.void ConnParams::writeVersion(rdr::OutStream* os)9.{10.char str[13];11. sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);12. os->writeBytes(str, 12);13. os->flush();14.}这里对应的发送的内容如下00000000 52 46 42 20 30 30 33 2E 30 30 38 0A RFB 003.008.发送数据长度12发送数据内容RFB 003.008此时VNC 的数据处理状态为RFBSTATE_PROTOCOL_VERSION2.viewer收到来自vnc的版本号信息因为viewer在init()函数中已经设置数据处理状态为RFBSTATE_PROTOCOL_VERSION [cpp]view plaincopyprint?1.void CConnection::initialiseProtocol()2.{3. state_ = RFBSTATE_PROTOCOL_VERSION;4.}[cpp]view plaincopyprint?1.void CConnection::processMsg()2.{3.switch (state_)4. {5.case RFBSTATE_PROTOCOL_VERSION:6. processVersionMsg();7.break;8.case RFBSTATE_SECURITY_TYPES:9. processSecurityTypesMsg();10.break;11.case RFBSTATE_SECURITY:12. processSecurityMsg();13.break;14.case RFBSTATE_SECURITY_RESULT:15. processSecurityResultMsg();16.break;17.case RFBSTATE_INITIALISATION:18. processInitMsg();19.break;20.case RFBSTATE_NORMAL:21. reader_->readMsg();22.break;23.case RFBSTATE_UNINITIALISED:24.throw Exception("CConnection::processMsg: not initialised yet?");25.default:26.throw Exception("CConnection::processMsg: invalid state");27. }28.}[cpp]view plaincopyprint?1.进入 processVersionMsg()函数2.//处理 RFBSTATE_PROTOCOL_VERSION3.void CConnection::processVersionMsg()4.{5. vlog.debug("reading protocol version");6.bool done;7.//这里读取服务器版本,如果读取失败数据处理状态切换为 RFBSTATE_INVALID8.if (!cp.readVersion(is, &done))9. {10. state_ = RFBSTATE_INVALID;11.throw Exception("reading version failed:not an RFB server?");12. }13.//读取失败返回14.if (!done)15.return;16.17. ("Server supports RFB protocol version %d.%d",18. cp.majorVersion, cp.minorVersion);19.20.// The only official RFB protocol versions are currently 3.3, 3.7 and 3.821.//如果RFB版本号低于 3.3 不支持。

数据处理状态切换为 RFB_INVALID 抛出异常22.if (cp.beforeVersion(3, 3))23. {24.char msg[256];25. sprintf(msg, "Server gave unsupported RFB protocol version %d.%d",26. cp.majorVersion, cp.minorVersion);27. vlog.error(msg);28. state_ = RFBSTATE_INVALID;29.throw Exception(msg);30. }31.else if (useProtocol3_3 || cp.beforeVersion(3, 7))32. {33. cp.setVersion(3, 3);34. }35.else if (cp.afterVersion(3, 8))36. {37. cp.setVersion(3, 8);38. }39.//这里写入版本号信息40. cp.writeVersion(os);41.//Viewer切换数据处理状态 RFBSTATE_SECURITY_TYPES42. state_ = RFBSTATE_SECURITY_TYPES;43.44. ("Using RFB protocol version %d.%d",45. cp.majorVersion, cp.minorVersion);46.}47.//向服务器发送版本号48.void ConnParams::writeVersion(rdr::OutStream* os)49.{50.char str[13];51. sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);52. os->writeBytes(str, 12);53. os->flush();54.}这个函数发送的内容00000000 52 46 42 20 30 30 33 2E 30 30 38 0A RFB 003.008.长度为12 个字节这时候VNCViewer的数据处理状态为RFBSTATE_SECURITY_TYPES3.vnc收到来自viewer的版本信息因为VNC当前的数据处理状态是RFBSTATE_PROTOCOL_VERSION所以会进入processVersionMsg()函数处理viewer发送过来的版本信息[cpp]view plaincopyprint?1.//处理 RFBSTATE_PROTOCOL_VERSION2.void SConnection::processVersionMsg()3.{4. vlog.debug("reading protocol version");5.bool done;6.//读取来自viewer的版本号7.if (!cp.readVersion(is, &done))8. {9. state_ = RFBSTATE_INVALID;10.throw Exception("reading version failed:not an RFB client?");11. }12.if (!done)13.return;14.15. ("Client needs protocol version %d.%d",cp.majorVersion, cp.minorVersion);16.17.if (cp.majorVersion != 3)18. {19.// unknown protocol version20.char msg[256];21. sprintf(msg, "Error: client needs protocol version %d.%d, server has %d.%d",cp.majorVersion, cp.minorVersion,defaultMajorVersion, defaultMinorVers ion);22. throwConnFailedException(msg);23. }24.25.if (cp.minorVersion != 3 && cp.minorVersion!= 7 && cp.minorVersion != 8)26. {27. vlog.error("Client uses unofficial protocol version %d.%d",cp.majorVersion, cp.minorVersion);28.if (cp.minorVersion >= 8)29. cp.minorVersion = 8;30.else if (cp.minorVersion == 7)31. cp.minorVersion = 7;32.else33. cp.minorVersion = 3;34. vlog.error("Assuming compatibility withversion %d.%d",cp.majorVersion, cp.minorVersion);35. }36.37. versionReceived();38.39.//这里是获取到加密类型40. std::list<rdr::U8> secTypes;41. std::list<rdr::U8>::iterator i;42. securityFactory->getSecTypes(&secTypes, reverseConnection);43.44.if (cp.isVersion(3, 3))45. {46.// cope with legacy 3.3 client only if "no authentication" or "vnc47.// authentication" is supported.48.for (i = secTypes.begin(); i != secTypes.end(); i++)49. {50.if (*i == secTypeNone || *i == secTypeVncAuth)51.break;52. }53.if (i == secTypes.end())54. {55.char msg[256];56. sprintf(msg, "No supported securitytype for %d.%d client",57. cp.majorVersion, cp.minorVersion);58. throwConnFailedException(msg);59. }60.61. os->writeU32(*i);62.if (*i == secTypeNone)63. os->flush();64. state_ = RFBSTATE_SECURITY;65. security = securityFactory->getSSecurity(*i, reverseConnection);66. processSecurityMsg();67.return;68. }69.70.// list supported security types for >=3.7 clients71.if (secTypes.empty())72. throwConnFailedException("No supported security types");73.74. os->writeU8(secTypes.size());75.for (i = secTypes.begin(); i != secTypes.end(); i++)76. os->writeU8(*i);77. os->flush();78. state_ = RFBSTATE_SECURITY_TYPE;79.}80.这个函数主要是81. a.验证RFB版本号82. b.获取到加密类型83. c.发送加密类型到VncViewer84. d.设置数据处理标志为 RFBSTATE_SECURITY_TYPE4.vncViewer状态为RFBSTATE_SECURITY_TYPES收到vnc发送过来的加密类型后执行processSecurityTypesMsg()[cpp]view plaincopyprint?1.void CConnection::processSecurityTypesMsg()2.{3. vlog.debug("processing security types message");4.5.int secType = secTypeInvalid;6.7.if (cp.isVersion(3, 3))8. {9.10.// legacy 3.3 server may only offer "vncauthentication" or "none"11. secType = is->readU32();12.if (secType == secTypeInvalid)13. {14. throwConnFailedException();15. }16.else if (secType == secTypeNone || secType == secTypeVncAuth)17. {18.int j;19.for (j = 0; j < nSecTypes; j++)20.if (secTypes[j] == secType)21.break;22.if (j == nSecTypes)23. secType = secTypeInvalid;24. }25.else26. {27. vlog.error("Unknown 3.3 security type %d", secType);28.throw Exception("Unknown 3.3 security type");29. }30.31. }32.else33. {34.// >=3.7 server will offer us a list35.int nServerSecTypes = is->readU8();36.if (nServerSecTypes == 0)37. throwConnFailedException();38.39.int secTypePos = nSecTypes;40.for (int i = 0; i < nServerSecTypes; i++)41. {42. rdr::U8 serverSecType = is->readU8();43. vlog.debug("Server offers security type %s(%d)",44. secTypeName(serverSecType), serverSecType);45.46.// If we haven't already chosen a secType, try this one47.// If we are using the client's preference for types,48.// we keep trying types, to find theone that matches and49.// which appears first in the client's list of supported types.50.if (secType == secTypeInvalid || clientSecTypeOrder)51. {52.for (int j = 0; j < nSecTypes; j++)53. {54.if (secTypes[j] == serverSecType && j < secTypePos)55. {56. secType = secTypes[j];57. secTypePos = j;58.break;59. }60. }61.// NB: Continue reading the remaining server secTypes, but ignore them62. }63. }64.65.// Inform the server of our decision66.if (secType != secTypeInvalid)67. {68. os->writeU8(secType);69. os->flush();70. vlog.debug("Choosing security type %s(%d)", secTypeName(secType), secType);71. }72. }73.74.if (secType == secTypeInvalid)75. {76. state_ = RFBSTATE_INVALID;77. vlog.error("No matching security types");78.throw Exception("No matching security types");79. }80.81. state_ = RFBSTATE_SECURITY;82.//因为这里没有开启认证,所以security返回的是一个CSecurityNone类83. security = getCSecurity(secType);84.//调用getCSecurity函数进行下一步的认证工作85. processSecurityMsg();86.}87.88.CSecurity* CConn::getCSecurity(int secType)89.{90.switch (secType)91. {92.case secTypeNone:93.return new CSecurityNone();94.case secTypeVncAuth:95.return new CSecurityVncAuth(this);96.default:97.throw Exception("Unsupported secType?");98. }99.}这个函数a.获取加密类型b.切换数据处理状态为RFBSTATE_SECURITYC.生成用例处理加密的类d.调用processSecurityMsg()函数继续处理加密[cpp]view plaincopyprint?1.void CConnection::processSecurityMsg()2.{3. vlog.debug("processing security message");4.if (security->processMsg(this))5. {6. state_ = RFBSTATE_SECURITY_RESULT;7. processSecurityResultMsg();8. }9.}因为我这里没有开启认证,所以processMsg()函数直接返回TRUE就通过验证。

相关主题