TightVNC分析文檔

來源:互聯網
上載者:User

System Shell :
1.1 IActiveDesktop
    Allows a client program to manage the desktop items and wallpaper on a local computer.

#include 

IActiveDesktop* active_desktop = 0;
CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveDesktop, (void**)&active_desktop);


1.2 SetProcessShutdownParameters
    The SetProcessShutdownParameters
function sets shutdown parameters for the currently calling process.
This function sets a shutdown order for a process relative to the other
processes in the system.

// Set this process to be the last application to be shut down.
SetProcessShutdownParameters(0x100, 0);


1.3 One Instance Running by Mutex
    Use Win32 Mutex object to insure that only one instance is currently running in our OS.

const char mutexname [] = "WinVNC_Win32_Instance_Mutex";

BOOL vncInstHandler::Init()
{
// Create the named mutex
HANDLE mutex = CreateMutex(NULL, FALSE, mutexname);
if (mutex == NULL)
return FALSE;

// Check that the mutex didn't already exist
if (GetLastError() == ERROR_ALREADY_EXISTS)
return FALSE;

return TRUE;
}


1.4 sscanf
    Read formatted data from a string.

// Check the protocol version
int major, minor;
sscanf((char *)&protocol_ver, "RFB %03d.%03d/n", &major, &minor);


1.5 Kill Screen Saver

// How to kill the screen saver depends on the OS
switch (osversioninfo.dwPlatformId)
case VER_PLATFORM_WIN32_WINDOWS:
HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
if (hsswnd != NULL)
PostMessage(hsswnd, WM_CLOSE, 0, 0);
break;

case VER_PLATFORM_WIN32_NT:
HDESK hDesk = OpenDesktop(
"Screen-saver",
0,
FALSE,
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
);
if (hDesk != NULL)
{
EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
CloseDesktop(hDesk);
// Pause long enough for the screen-saver to close
//Sleep(2000);
// Reset the screen saver so it can run again
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE);
}
break;
}


1.6 Disable Nagle Algorithm
    Nagle
Algorithm主要是用於最佳化小資料包的發送,用於在IP棧中緩衝小資料包,積累一定數量的小資料包一起發送。這樣可以減少系統發包的數
量,Nagle
Algorithm實現時內部也有一個Timeout機制,但這個Timeout時間長度不能在外部設定。而且對每個IP棧的實現,這個Timeout
Span 都有一個實現相關的值。

// Disable Nagle's algorithm
BOOL nodelayval = TRUE;
setsockopt(m_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&nodelayval, sizeof(BOOL));

Screen Capture :
2.1 Poll Schema
    Poll
方式就是最簡單的一種方式了,也是大家在考慮截屏時最先想到一個解決方案。為取得系統的螢幕,poll方案每33ms輪詢一次系統螢幕,將變化的部分添加
加到一個UpdateRegion中。為擷取這些螢幕變化的Rect,系統還採取了一種最佳化Polling演算法,將螢幕分為32*32
pixel的小矩形塊,演算法給出了一個各個矩形塊輪詢的Order:

const int pollingOrder[32] = {
0, 16, 8, 24, 4, 20, 12, 28,
10, 26, 18, 2, 22, 6, 30, 14,
1, 17, 9, 25, 7, 23, 15, 31,
19, 3, 27, 11, 29, 13, 5, 21
};

擷取當前螢幕滑鼠映像的思路:
第一步:擷取螢幕滑鼠的HCURSOR
    GetCursorPos擷取屏
幕滑鼠位置,然後WindowFromPoint擷取滑鼠的擁有者視窗,GetWindowThreadProcessId擷取相關線程ID,比較該ID
和GetCurrentThreadId返回的ID,如果相同,通過GetCursor直接擷取滑鼠的HCURSOR;否則,通過
AttachThreadInput串連上目標線程的Input Mechanism,再調用GetCursor擷取滑鼠的HCURSOR。
第二步:擷取HCURSOR的Bitmap

int :GetCursorSendBuffer(BYTE *pBuffer, int nSize)
{
....
ICONINFO IconInfo;
GetIconInfo(hcursor, &IconInfo);

BITMAP bmMask;
GetObject(IconInfo.hbmMask, sizeof(BITMAP), (LPVOID)&bmMask);
GetBitmapBits(IconInfo.hbmMask,bmMask.bmWidthBytes * bmMask.bmHeight, m_mBits);
....
}


現在有必要對HRGN操作相關API做一下介紹

GetRgnBox      擷取HRGN的邊界矩形
GetRegionData 可以將一個HRGN分解為一個RECT數組,見RGNDATA結構體說明
CombineRgn 對HRGN操作RGN_AND,RGN_COPY,RGN_DIFF,RGN_OR或是RGN_XOR

2.2 TightVNC
    TightVNC(Tight Virtual Network Computing)是一個遠端桌面控制的開源軟體,詳情請參考http://www.tightvnc.com.下載了TightVNC的代碼,分析了一下其Server部分的代碼, WinVNC下的檔案很多,但我們按照它們各自的功能做一下劃分,其結構如下:

Kernel vncBuffer.cpp vncClient.cpp vncDesktop.cpp vncServer.cpp WinVNC.cpp
GUI vncAbout.cpp vncAcceptDialog.cpp vncAdvancedProperties.cpp vncConnDialog.cpp vncMenu.cpp vncProperties.cpp vncTimedMsgBox.cpp
Misc d3des.c
Log.cpp MinMax.cpp RectList.cpp stdhdrs.cpp tableinitcmtemplate.cpp
tableinittctemplate.cpp tabletranstemplate.cpp translate.cpp vncauth.c
vncInstHandler.cpp vncKeymap.cpp vncRegion.cpp< vncService.cpp
Network VSocket.cpp vncSockConnect.cpp vncHTTPConnect.cpp rfbproto.h
Encoding vncEncodeCoRRE.cpp vncEncodeHexT.cpp vncEncoder.cpp vncEncodeRRE.cpp vncEncodeTight.cpp vncEncodeZlib.cpp vncEncodeZlibHex.cpp

其服務端的主要功能模組結構如下:
其核心架構就是四個類vncClient,vncServer,vncDesktop和vncBuffer.下面我就這四個類之間的聯絡和用途來作一下簡單的分析:
vncServer:
    vncServer
主要是做如下的一些工作:容許vncClient動態添加和刪除;將本地vncDesktop對象內部狀態的任何改變"傳播"到各個用戶端;傳播用戶端
的滑鼠和鍵盤事件到本地的vncDesktop對象。同時,其還建立了vncSockConnect,vncCORBAConnect和
vncHTTPConnect來接受Socket,Corba和HTTP的串連。
vncServer為每個串連上來的用戶端分配了一個ClientID(其實就是內部客戶對象數組的Index),並且提供了對用戶端管理的眾多函數:

virtual void DisableClients(BOOL state);
virtual void KillClient(vncClientId client);
virtual void KillAuthClients();
virtual void KillUnauthClients();

virtual vncClient* GetClient(vncClientId clientid);
vncClientId AddClient(VSocket *socket, BOOL auth, BOOL shared);
virtual void RemoveClient(vncClientId client);

同時,vncServer還提供了對客戶Teleport,Capability,KeyboardEnabled,PointerEnabled,Name,Authenticated屬性的get/set方法。
下面我們來看一下vncServer對用戶端串連上來和用戶端認證成功這兩個事件的處理流程:
vncServer::AddClient:
    首
先vncServer在其內部的vncClient
*m_clientmap[MAX_CLIENTS]數組中為新串連上的用戶端分配一個閒置slot,並將其作為此客戶的 clientID.
然後,為此串連分配一個vncClient對象,根據傳遞過來的參數,設定vncClient對象的相關屬性,然後調用vncClient::Init方
法將vncServer的執行個體指標和 clientID傳給vncClient執行個體。接著,m_clientmap[clientid] =
client並將此使用者加入vncServer的未認證使用者鏈表。
vncServer::Authenticated(vncClientId clientid):
    首
先從未認證使用者列表中根據clientid擷取vncClient對象,並將其從unauth list
中刪除。如果是vncServer的第一個使用者,建立vncDesktop對象,並調用m_desktop->Init(this)來初始化該
vncDesktop對象。接下來,為這個使用者指派一個vncBuffer *buffer = new
vncBuffer(m_desktop);並通過調用vncClient::SetBuffer為vncClient設定這個Buffer,最後將此用
戶添加到auth list中。
    vncServer提供了一個使用者列表的操作介面,這些介面通過將vncServer的方法調用映射到對auth list中各個客戶的同樣的方法的函數調用,這些方法有:

virtual void TriggerUpdate();
virtual void UpdateRect(RECT &rect);
virtual void UpdateRegion(vncRegion ion);
virtual void CopyRect(RECT &dest, POINT &source);
virtual void UpdateMouse();
virtual void UpdateClipText(LPSTR text);
virtual void UpdatePalette();

vncDesktop:
    vncDesktop是
一個全域唯一的對象,根據注釋,vncDesktop主要是處理從display buffer中擷取資料;同時,它還利用RFBLib
DLL為vncServer提供諸如滑鼠移動和螢幕更新等資訊。上面提到,vncServer在第一個使用者串連上來時發現其m_desktop為空白時就創
建一個vncDesktip對象,並調用
vncDesktop::Init(this)對其初始化.在vcnDesktop::Init的實現中我們發現其建立了一個
vncDesktopThread,vncDesktop的方法調用大部分都在這個vncDesktopThread裡完成的.下面我們來分析一下這個線
程都做了些什麼:
vncDesktopThread::run_undetached(void *arg):
    首
先調用vncDesktop::Startup初始化,vncDesktop對象(見vncDesktop::Startup),然後就是處理案頭訊息,
調用 m_server->UpdateMouse()和m_server->UpdateRegion(rgncache)
,接下來調用vncServer::TriggerUpdate來發送螢幕更新到每個vncClient.然後就是處理RFB_SCREEN_UPDATE和RFB_MOUSE_UPDATE這兩個註冊訊息。

vncClient:
    vncClient做了資料發送的工作,在vncClient::SendUpdate函數的實現中,我們可以看到vncClient調用SendRFBMsg首先發送 ,然後SendCursorShapeUpdate發送滑鼠形狀更新,SendCursorPosUpdate發送滑鼠Pos更新,發送SendCopyRect,最後調用SendRectangles發
送需要更新的矩形的相關資料。其實每個用戶端vncClient在調用vncClient::Init初始化的時候都開了一個線程,用戶端的行為基本上都
是在vncClientThread::run裡完成的。該線程在跟用戶端互動完成了認證,Pixel格式,Encoding演算法等資訊的協商後,就進入
一個loop迴圈開始接受和處理遠程用戶端發過來的
rfbSetPixelFormat,rfbSetEncodings,rfbFramebufferUpdateRequest,rfbKeyEvent,rfbPointerEvent,rfbClientCutText
訊息。

vncBuffer:
    vncBuffer主要處理髮送資料的Encoding工作,其提供了遠程客戶的本地視圖,其主要是利用內部的vncDesktop指標來擷取相關的資料。

Reference:

  • http://www.tightvnc.com/
  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.