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构造函数就添加了三个事件到事件队列中。

VNCServerWin32run函数代码如下:

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,分别监听5900tcp port)和5800http-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系统。当由客户端请求连接时,就会调用SocketManagerprocessEvent函数,来创建一个新的tcpsocket与客户端进行连接,并将此tcpsocket添加到SocketManager中,将与此tcpsocket关联的事件添加到事件队列中,这样一来,VNC系统就可以处理新建tcpsocket的事件了。

当客户端与服务器端建立连接之后,服务器端和客户端就需要基于RBF协议就行会话通信。接下来分析一下RealVNC实现RFB协议的具体过程。

2、RealVNC服务器端RFB协议的实现

通过以上的分析可知当由套接字事件被触发时将会调用SocketManagerprocessEvent

函数:

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协议serverVNCServerST类的对象

     ci.server->processSocketEvent(ci.sock);

     if (ci.sock->isShutdown()){

       remSocket(ci.sock);

       return;

     }

   ……//省略部分代码

 }

}

SocketManager::processEvent的源代码可知,processEvent函数通过调用会与该套接字的服务器类VNCServerSTprocessSocketEvent函数来处理已连接的套接字收发(即RFB协议)。接下来看一下VNCServerSTprocessSocketEven的源码:

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){

  //调用VNCSConnectionSTprocessMessages()函数处理套接字事件

     (*ci)->processMessages();

     return;

   }

 }

 throw rdr::Exception("invalidSocket in VNCServerST");

}

VNCSConnectionST类继承自SConnection,是为VNCServerST设计的一个客户端连接类。每一个已连接的客户端(套接字)都拥有一个VNCSConnectionST类对象。可以认为VNCSConnectionST类就是用来维护、处理与客户端连接的套接字的。VNCSConnectionSTprocessMessage源码如下:

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());

 }

}

VNCSConnectionSTprocessMessage函数中从要处理的已连接套接字读数据,当可以读到数据时,说明客户端向服务器端发送了信息,这时就调用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可正式这么做的,在建立与客户端的连接后就立即调用VNCSConnectionSTinit函数从而调用SConnection::initialiseProtocol()函数先客户端向发送RFB版本号,然后将RFB状态设为RFBSTATE_PROTOCOL_VERSION。在SConnection::processMsg()函数中根据RFB当前的状态,调用相应的函数与客户端通信,并在调用的函数中修改RFB状态。这样就实现了RFB协议。

0 0
原创粉丝点击