VNC的圖像更新機(jī)制核心為,桌面區(qū)域更新記錄策略和更新區(qū)域通知策略。桌面更新區(qū)域記錄主要是通過(guò)hooks記錄桌面上變化的矩形區(qū)域,只記錄更新的矩形區(qū)不記錄具體更新的數(shù)據(jù)。更新區(qū)域記錄步驟大致如下:1.wm_hooks截獲桌面變化的相關(guān)消息,并轉(zhuǎn)化為自定義的消息發(fā)送給WMHooksThread線程處理。 2. WMHooksThread 中用SimpleUpdateTracker new_changes記錄新的更新區(qū)域.3.把SimpleUpdateTracker new_changes更新拷貝到SDisplay中。4.每次要發(fā)送桌面更新的時(shí)候,把SDisplay中記錄的更新區(qū)域傳給VNCServerST 對(duì)象中。更新區(qū)域的通知主要有poll和push兩種機(jī)制。push是服務(wù)器每隔10ms檢查有沒(méi)有更新,如果有更新則主動(dòng)把更新推送給客戶端,poll機(jī)制則是客戶端主動(dòng)請(qǐng)求更新,客戶端通過(guò)發(fā)送framebufferupdate請(qǐng)求某一個(gè)區(qū)域更新,服務(wù)器處理該消息發(fā)送相應(yīng)的更新。詳細(xì)分析如下:
1.Wm_hooks截獲消息并轉(zhuǎn)化為自定義的消息發(fā)送給WMHooksThread線程處理。
Wm_hooks自定義的消息:
UINT WM_HK_WindowChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowChanged"));
UINT WM_HK_WindowClientAreaChanged = UINT WM_HK_WindowBorderChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.WindowBorderChanged"));
UINT WM_HK_RectangleChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.RectangleChanged"));
UINT WM_HK_CursorChanged = RegisterWindowMessage(_T("RFB.WM_Hooks.CursorChanged"));
鉤子截獲到消息以后,把它轉(zhuǎn)化為自定義的消息,然后發(fā)送給WMHooksThread線程處理,消息轉(zhuǎn)化如下:
邊框更新消息:WM_NCPAINT,WM_NCACTIVATE
客戶區(qū)域更新消息:BM_SETCHECK, BM_SETSTATE,EM_SETSEL,WM_CHAR,WM_ENABLE,WM_KEYUP,WM_LBUTTONUP,WM_MBUTTONUP,WM_PALETTECHANGED,WM_RBUTTONUP,WM_SYSCOLORCHANGE,WM_SETTEXT。
窗口改變消息:WM_HSCROLL,WM_VSCROLL,482,485。
矩形區(qū)更新消息:WM_DESTROY
窗口客戶區(qū)消息:WM_PAINT
鼠標(biāo)消息:WM_NCMOUSEMOVE,WM_MOUSEMOVE
2 . WMHooksThread 中用SimpleUpdateTracker new_changes記錄新的更新區(qū)域
WMHooksThread::run() 函數(shù)中先判斷出矩形區(qū)域改變的大小,然后調(diào)用NotifyHooksRegion(const Region& r)把改變的區(qū)域記錄到SimpleUpdateTracker new_changes中。
NotifyHooksRegion(const Region& r) {
Lock l(hook_mgr_lock);
std::list<WMHooks*>::iterator i;
for (i=hooks.begin(); i!=hooks.end(); i++) {
(*i)->new_changes.add_changed(r);
if (!(*i)->notified) {
(*i)->notified = true;
PostMessage((*i)->getHandle(), WM_USER, 0, 0); // 把消息通知到clipper見(jiàn)下面一個(gè)處理函數(shù)
}
}
}
3.把更新區(qū)域拷貝到SDisplay中
rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_USER:
{
Sleep(0);
Lock l(hook_mgr_lock);
notified = false;
new_changes.get_update(*clipper); //把更新通知到clipper中
new_changes.clear();
}
break;
}
return MsgWindow::processMessage(msg, wParam, lParam);
}
Cliper在下面設(shè)置
rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
if (clipper) delete clipper;
clipper = new ClippedUpdateTracker(*ut);
clipper->set_clip_region(clip_region);
return AddHook(this);
}
UpdateTracker* ut 為void SDisplay::start(VNCServer*vs)中設(shè)置 core->using_hooks = core->wm_hooks.setUpdateTracker(this);
4.把SDisplay中記錄的數(shù)據(jù)傳給VNCServerST 對(duì)象
在 SDisplay::processEvent(HANDLE event) {
try_update = flushChangeTracker() || try_update; //把變化的區(qū)域拷貝到VNCServerST中
if (try_update)
server->tryUpdate(); //把更新發(fā)送給服務(wù)器
}
flushChangeTracker()實(shí)現(xiàn)如下:
bool SDisplay::flushChangeTracker() {
if (change_tracker.is_empty())
return false;
change_tracker.translate(screenRect.tl.negate());
change_tracker.get_update(*server); //server 實(shí)際指向VNCServerST 對(duì)象該函數(shù)把SDisplay中的更新拷貝到VNCServerST中。
change_tracker.clear();
return true;
}
兩種數(shù)據(jù)更新方式:Push機(jī)制和Pull機(jī)制
Push:
SdisplayCore 中IntervalTimer cursorTimer定時(shí)器,每隔10ms嘗試著檢查一下是否有更新,如果有更新就發(fā)送更新給客戶端。
第一步:
LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
case TIMER_CURSOR:
display->triggerUpdate(); //SDisplay* display;
}
第二步:
void SDisplay::triggerUpdate() {
if (core)
SetEvent(updateEvent); //使事件對(duì)象為受信狀態(tài)
}
第三步:
SDisplay::processEvent(HANDLE event) {
if (event == updateEvent) {
if (try_update)
server->tryUpdate(); // VNCServer* server指針 指向子類VNCServerST
}
}
第四步:向每一個(gè)連接的客戶端發(fā)送更新
void VNCServerST::tryUpdate()
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->writeFramebufferUpdateOrClose();
}
}
第五步:
void VNCSConnectionST::writeFramebufferUpdateOrClose()
{
try {
writeFramebufferUpdate();
} catch(rdr::Exception &e) {
close(e.str());
}
}
第六步:SimpleUpdateTracker updates對(duì)象記錄更新的區(qū)域,如果屏幕有更新則發(fā)送更新
void VNCSConnectionST::writeFramebufferUpdate(){
if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);
writer()->writeFramebufferUpdateStart(nRects);
Region updatedRegion;
writer()->writeRects(update, &image_getter, &updatedRegion); // SmsgWriter *
updates.subtract(updatedRegion);
if (drawRenderedCursor)
writeRenderedCursorRect();
writer()->writeFramebufferUpdateEnd();
requested.clear();
}
}
第七步:利用RFB協(xié)議發(fā)送更新
void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
{
startMsg(msgTypeFramebufferUpdate);
os->pad(1);
if (wsccb) nRects++;
if (needSetDesktopSize) nRects++;
os->writeU16(nRects);
nRectsInUpdate = 0;
nRectsInHeader = nRects;
if (wsccb) {
wsccb->writeSetCursorCallback();
wsccb = 0;
}
|