RealVNC源码学习笔记 三
来源:互联网 发布:linux小红帽镜像 编辑:程序博客网 时间:2024/06/05 06:17
RealVNC源码学习笔记 三
1、RealVNC服务器端代码流程解析
RealVNC服务器端的main函数在winvnc模块的winvnc.cxx文件中,main函数很简单,该函数前面都是一些log信息设置,重点是构造VNCServerWin32对象后运行,该对象的run函数。VNCServerWin32构造函数如下:
VNCServerWin32::VNCServerWin32()
: command(NoCommand),commandSig(commandLock),
commandEvent(CreateEvent(0,TRUE, FALSE, 0)),
vncServer(CStr(ComputerName().buf), &desktop),
hostThread(0), runServer(false),isDesktopStarted(false),
httpServer(&vncServer),config(&sockMgr), trayIcon(0),
rfbSock(&sockMgr),httpSock(&sockMgr),
queryConnectDialog(0)
{
// Initialise the desktop
desktop.setStatusLocation(&isDesktopStarted);
// Initialise the VNC server
vncServer.setQueryConnectionHandler(this);
// Register the desktop's event tobe handled
//添加SDisplay事件到事件队列中(SDisplay继承自SDesktop)
sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
// Register the queued commandevent to be handled
//添加VNCServerWin32的事件到事件队列中
sockMgr.addEvent(commandEvent,this);
}
在VNCServerWin32的参数初始化列表中会构造RegConf类,RegConf构造函数中会添加一一个Regconf事件到事件队列中。这样经过VNCServerWin32构造函数就添加了三个事件到事件队列中。
VNCServerWin32的run函数代码如下:
int VNCServerWin32::run() {
//
{ Lock l(runLock);
hostThread = Thread::self();
runServer = true;
}
// - Create the tray icon (ifpossible)
//创建处理任务栏通知区VNC图标
trayIcon = newSTrayIconThread(*this, IDI_ICON, IDI_CONNECTED, IDR_TRAY);
// - Register for notification ofconfiguration changes
config.setCallback(this);
if (isServiceProcess())
config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
else
config.setKey(HKEY_CURRENT_USER,RegConfigPath);
// - Set the address-changedhandler for the RFB socket
//通过层层调用,会创建两个监听tcp,分别监听5900(tcp port)和5800(http-port)端口
//并将套接字关联的事件加入到事件队列中
rfbSock.setAddressChangeNotifier(this);
DWORD result = 0;
try {
vlog.debug("Enteringmessage loop");
// - Run the server until we'retold to quit
MSG msg;
int result = 0;
//主循环
while (runServer) {
//获得消息,在getMessage中会调用消息(事件)的拥有这对消息(事件)进行处理
result =sockMgr.getMessage(&msg, NULL, 0, 0);
if (result < 0)
throwrdr::SystemException("getMessage", GetLastError());
if (!isServiceProcess()&& (result == 0))
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
vlog.debug("Server exitedcleanly");
} catch (rdr::SystemException&s) {
vlog.error(s.str());
result = s.err;
} catch (rdr::Exception &e) {
vlog.error(e.str());
}
{ Lock l(runLock);
runServer = false;
hostThread = 0;
}
return result;
}
在run函数中,由添加两个tcp事件到事件队列中。这样就在事件队里中添加了五个事件。也就是说在没有客户端连接之前,RealVNC服务器的时间队列中有五个事件。这五个事件的详情如下表:
序号
添加位置
事件拥有者类
事件用途
1
VNCServerWin32构造函数
Regconf
配置有更新时触发
2
VNCServerWin32构造函数
SDisplay
wm_hook.dll截获有用消息时触发
3
VNCServerWin32构造函数
VNCServerWin32
VNCServerWin32命令行触发
4
VNCServerWin32::run函数
SocketManager(与监听套接字关联 监听端口5900)
由客户端请求连接时触发
5
VNCServerWin32::run函数
SocketManager(与监听套接字关联 监听端口5800)
由http型客户端请求连接时触发
事件2 SDisplay事件是怎么反应wm_hook.dll消息的,请参看“源码学习笔记一”。再来看一下run函数中调用的GetMessages函数:
BOOL EventManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINTmaxMsg) {
while (true)
{
// - Process any pending timeouts
DWORD timeout = checkTimeouts();
if (timeout == 0)
timeout = INFINITE;
// - Events take precedence over messages
DWORD result;
if (eventCount)
{
// - Check whether any events are set
//等待事件列表中有事件被触发
result = WaitForMultipleObjects(eventCount,events, FALSE, 0);
if (result == WAIT_TIMEOUT) //等待超时
{
// - No events are set, so check for messages
if (PeekMessage(msg, hwnd, minMsg, maxMsg,PM_REMOVE))
return msg->message != WM_QUIT;
// - Block waiting for an event to be set, ora message
result = MsgWaitForMultipleObjects(eventCount,events, FALSE, timeout,
QS_ALLINPUT);
if (result == WAIT_OBJECT_0 + eventCount)
{
// - Return the message, if any
if (PeekMessage(msg, hwnd, minMsg, maxMsg,PM_REMOVE))
return msg->message != WM_QUIT;
continue;
}
}
}
else
return GetMessage(msg, hwnd, minMsg, maxMsg);
//事件队列中的某个事件被触发
if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0+ eventCount)))
{
// - An event was set - call the handler
//计算被触发的事件在事件队列中的位置
int index = result - WAIT_OBJECT_0;
//调用被触发事件的拥有者的processEvent函数,处理事件。
handlers[index]->processEvent(events[index]);
}
else if (result == WAIT_FAILED)
{
// - An error has occurred, so return theerror status code
return -1;
}
}
}
从getMessage函数可以看出,当事件被触发后,事件的拥有者来对该事件进行处理。这样就通过事件驱动了整个vnc系统。当由客户端请求连接时,就会调用SocketManager的processEvent函数,来创建一个新的tcpsocket与客户端进行连接,并将此tcpsocket添加到SocketManager中,将与此tcpsocket关联的事件添加到事件队列中,这样一来,VNC系统就可以处理新建tcpsocket的事件了。
当客户端与服务器端建立连接之后,服务器端和客户端就需要基于RBF协议就行会话通信。接下来分析一下RealVNC实现RFB协议的具体过程。
2、RealVNC服务器端RFB协议的实现
通过以上的分析可知,当由套接字事件被触发时,将会调用SocketManager的processEvent
函数:
void SocketManager::processEvent(HANDLE event) {
//判断事件是监听套接字的事件,还是已连接套接字事件
if (listeners.count(event)) {//监听套接字的事件
……//省略部分代码
} else if(connections.count(event)) {//已连接套接字事件
//获得已连接套接字信息
ConnInfo ci =connections[event];
try {
// Process data from an activeconnection
// Cancel event notificationfor this socket
if (WSAEventSelect(ci.sock->getFd(),event, 0) == SOCKET_ERROR)
throwrdr::SystemException("unable to disable WSAEventSelect:%u",WSAGetLastError());
// Reset the event object
WSAResetEvent(event);
// Call the socket server toprocess the event
//调用与该套接字关联的服务器对象的processSocketEvent,处理该套接字的收发,完成、//RFB协议server为VNCServerST类的对象
ci.server->processSocketEvent(ci.sock);
if (ci.sock->isShutdown()){
remSocket(ci.sock);
return;
}
……//省略部分代码
}
}
由SocketManager::processEvent的源代码可知,processEvent函数通过调用会与该套接字的服务器类VNCServerST的processSocketEvent函数来处理已连接的套接字收发(即RFB协议)。接下来看一下VNCServerST的processSocketEven的源码:
void VNCServerST::processSocketEvent(network::Socket* sock)
{
// - Find the appropriateVNCSConnectionST and process the event
std::list<VNCSConnectionST*>::iterator ci;
for (ci = clients.begin(); ci !=clients.end(); ci++) {
if ((*ci)->getSock() == sock){
//调用VNCSConnectionST的processMessages()函数处理套接字事件
(*ci)->processMessages();
return;
}
}
throw rdr::Exception("invalidSocket in VNCServerST");
}
VNCSConnectionST类继承自SConnection,是为VNCServerST设计的一个客户端连接类。每一个已连接的客户端(套接字)都拥有一个VNCSConnectionST类对象。可以认为VNCSConnectionST类就是用来维护、处理与客户端连接的套接字的。VNCSConnectionST的processMessage源码如下:
void VNCSConnectionST::processMessages()
{
if (state() == RFBSTATE_CLOSING)return;
try {
// - Now set appropriate sockettimeouts and process data
setSocketTimeouts();
//requested.is_empty()不为空时,clientsReadyBefore为真
bool clientsReadyBefore =server->clientsReadyForUpdate();
//查询输入流中是否有1个字节可读(以非阻塞方式),有返回真,没有返回false
while(getInStream()->checkNoWait(1)) {
//根据RBF的当前状态,进行响应处理
processMsg();
}
if (!clientsReadyBefore&& !requested.is_empty())
//通知当前客户端已经准备好跟新数据
server->desktop->framebufferUpdateRequest();
} catch (rdr::EndOfStream&) {
close("Cleandisconnection");
} catch (rdr::Exception &e) {
close(e.str());
}
}
VNCSConnectionST的processMessage函数中从要处理的已连接套接字读数据,当可以读到数据时,说明客户端向服务器端发送了信息,这时就调用processMsg函数,来处理客户端发来的信息。processMsg函数在VNCSConnectionST的父类SConnection中实现:
//根据RFB当前的状态,调用不同的函数
void SConnection::initialiseProtocol()
{
//发送RFB版本号给对方
cp.writeVersion(os);
state_ =RFBSTATE_PROTOCOL_VERSION;
}
void SConnection::processMsg()
{
switch (state_) {
case RFBSTATE_PROTOCOL_VERSION:processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_QUERYING:
throwException("SConnection::processMsg: bogus data from client while "
"querying");
case RFBSTATE_UNINITIALISED:
throwException("SConnection::processMsg: not initialised yet?");
default:
throwException("SConnection::processMsg: invalid state");
}
}
RFB协议规定,握手始于服务器向客户发送协议版本的消息,以告知客户服务器所能支持RFB协议的最高版本号。因此,在客户端与服务器建立连接后,服务器就应立即向客户端发送RFB版本号,RealVNC可正式这么做的,在建立与客户端的连接后就立即调用VNCSConnectionST的init函数从而调用SConnection::initialiseProtocol()函数先客户端向发送RFB版本号,然后将RFB状态设为RFBSTATE_PROTOCOL_VERSION。在SConnection::processMsg()函数中根据RFB当前的状态,调用相应的函数与客户端通信,并在调用的函数中修改RFB状态。这样就实现了RFB协议。
- RealVNC源码学习笔记 三
- RealVNC源码学习笔记 二
- RealVNC源码学习笔记 一
- nginx 源码学习笔记(三)
- dubbo源码 学习笔记(三)
- linux下源码安装realvnc
- Ubuntu编译vnc源码 realvnc
- linux下源码安装realvnc
- [源码学习] -- yii2源码学习笔记(三) -- Component代码详解
- Hadoop HDFS源码学习笔记(三)
- android 学习笔记三(android 源码编译)
- tomcat源码分析学习笔记(三)
- STL源码剖析学习笔记(三)
- 比特币源码学习笔记(三)
- 比特币源码学习笔记(三)
- jQuery源码学习笔记系列(三)
- spring源码学习笔记-初始化(三)-BeanFactory
- spring源码学习笔记-初始化(三) registerBeanPostProcessors
- Spring源代码解析(二):IoC容器在Web容器中的启动
- nginx搭建hls服务流程
- Editplus SVN的设置
- Java编程中“为了性能”需做的26件事
- 编译原理:tiny语言
- RealVNC源码学习笔记 三
- 剖析MFC多线程程序的同步机制
- R语言基本操作
- 利用ffmpeg压缩屏幕图像为avi(录屏、压制)
- Spring源代码解析(三):Spring JDBC
- CODEVS 1201 最小数和最大数
- Java线程安全和非线程安全
- Spring源代码解析(四):Spring MVC
- iOS开发网络篇—文件下载(一·不合理)