• 沒有找到結果。

Client 訊息處理迴圈 訊息處理迴圈 訊息處理迴圈 訊息處理迴圈

第四章 VNC Client

4.2 VNC Client 運作流程 運作流程 運作流程 運作流程

4.2.2 Client 訊息處理迴圈 訊息處理迴圈 訊息處理迴圈 訊息處理迴圈

一連串初始化過程後,進入迴圈前,Client 會先送一次畫面更新要求訊息,

要求 Server 立即送全螢幕的資料過來,接著,Client 開始專心處理 Server 送過 來的訊息。至於詳細處理封包格式部分,在第五章會再詳細說明。

表 4-1.Server 傳給 Client 的訊息種類 Number Name

0 FramebufferUpdate 1 SetColourMapEntries

2 Bell

3 ServerCutText 表 4-2.其他有定義的訊息種類

要使用非以上定義的訊息,Server 須收到 Client 回傳的確認訊息才可以.

Number Name

255 Anthony Liguori

254,127 VMWare

253 gii

4.3 VNC Viewer 細部工作原理 細部工作原理 細部工作原理 細部工作原理

這節是整章最重要的部分,因為一套遠端桌面系統的 Client 端,不外乎是送 滑鼠、鍵盤和要求畫面更新訊息給 Server,接著就是接收 Server 送過來的溝通 或畫面訊息,並將他們處理後顯示畫面在視窗上,因此,這節就是要針對這些動 作做分析。在 4.3.1 先說明 Client 收到 Server 畫面更新訊息解碼後,怎麼用 GDI 函式將這些像素顯示在視窗上,4.3.2 接著說明三種情況會觸發畫面更新要求訊 息:1.在 Client 畫面更新後會觸發,2.在 Viewer 視窗縮小還原後觸發,3.一開始 的觸發全螢幕更新,4.3.3 節則是針對 Client 如何將本地端鍵盤和滑鼠的動作,

轉成 RFB Protocol 訊息的方式傳送給 Server。以上說明後,就可完全了解 Client 一切的運作。

4.3.1 Viewer 畫面更新 畫面更新 畫面更新 畫面更新

要更新一個完整 FramebufferUpdate 訊息中的畫面,一共需要三個步驟,分 別是:1.解碼、2.更新像素資料至 Viewer 的 Framebuffer、3. InvalidateRect() 和 WM_PAINT,先由 FramebufferUpdate 得知有幾個矩形資料,再各自呼叫 解 碼 函 數 將 畫 面 資 料 解 出 , 並 更 新 到 Viewer 的 Framebuffer 上 , 再 用 InvalidateRect()函數將對應矩形位置無效化,此時 OS 會發送 WM_PAINT 給 Viewer 的訊息處理函數,在處理函數中將新的畫面資料更新到 Viewer 視窗上,

以上步驟一直不斷重覆,使用者即可獲得 Server 的最新畫面。

●1.解碼

在 Client 訊息處理迴圈中,當它判斷收到 FramebufferUpdate 訊息後,會呼 叫 ReadScreenUpdate()函數進行處理,一開始先讀出此更新訊息中共有幾個矩 形資料,接著用 for 迴圈一個一個再取出,如下圖藍框。每個矩形都有自己的標 頭,裡頭記錄著這個矩形它 x、y 座標、高、寬和編碼格式,因此,依照不同的

編碼格式呼叫不同的解碼函數將畫面資料解出,如下圖第一個紅框。

●2.更新像素資料至 Viewer 的 Framebuffer

在 Initialization 時,Client 已經建立了一塊 Bitmap 記憶體,用來存放 Client 的 Framebuffer,因此每個壓縮的矩形資料解碼後,會將解完的像素複製到這塊 記憶體上,重覆以上動作就可達到 Client 端 Framebuffer 更新。但至此,Viewer 視窗上的內容還不會更新,之後要再透過 BitBlt 函數將這些矩形部分的畫面,從 Bitmap 記憶體複製到螢幕,Client 的 Viewer 視窗才能看到變動後的畫面,這些 部分是以 InvalidateRect()來觸發 WM_PAINT 訊息,並在 Viewer 的視窗處理函 數中完成,接著會繼續說明。

圖 4-8.Viewer 畫面更新

●3.InvalidateRect()和 WM_PAINT

在解碼完一個小矩形畫面後,Bitmap 記憶體中對應區塊,已經記錄著最新的 畫面資料,接著要將這些區塊的像素更新到螢幕上,就要透過 GDI 的 BitBlt 函 數來達成。

圖 4-9.Copy to Screen

VNC Client 會先透過 InvalidateRect()來將 Viewer 對應的矩形位置區域無效 化,當有無效區域產生後,OS 會偵測到 Viewer 的某些地方需要更新重畫,OS 隨即發送 WM_PAINT 給 Viewer 視窗,請它對應的視窗處理函數 WndProc()處 理,當 WndProc 判斷是 WM_PAINT 訊息時,它便會從此訊息得到需要更新的 座標和區域大小,接著使用 BitBlt 函數,將 Bitmap 記憶體對應區塊的資料複製 到 螢 幕 , 程 式 碼 如 上 圖 。 先 用 SelectObject() 將 Bitmap 記 憶 體 同 步 至 m_hBitmapDC,再呼叫 BitBlt 函數把來源 m_hBitmapDC 的資料複製到螢幕的 Device Context,處理完成後,Viewer 視窗的矩形位置就會更新成最新畫面。

以上就是畫面如何更新。假設一個畫面更新訊息中有 5 個矩形的壓縮資料,

就要重覆以上動作 5 次才會完成一次的更新!

4.3.2 Viewer 觸發畫面更新要求 觸發畫面更新要求 觸發畫面更新要求 觸發畫面更新要求

接著這節要介紹,Client 在以下三種情況下會觸發畫面更新要求,分別是:

1. 一次完整畫面更新後;2.在 Viewer 視窗縮小還原後;3.一開始的全螢幕更新。

●1.在一次完整畫面更新後

延用上圖,在一次完整的更新後,即所有矩形資料皆解碼後,會跳離 for 迴 圈,在 ReadScreenUpdate 的尾端(橘色框),Client 以訊息的方式 Post 一個

WM_REGIONUPDATED 訊 息 給 Viewer 視 窗 程 式 , 它 所 屬 訊 息 處 理 函 數 WndProc 偵測到 WM_REGIONUPDATE 訊息之後,會先看 Viewer 狀態有否被 使用者縮小(下圖程式碼),這是因為 VNC 是 Client Pull,當判斷目前 Viewer 被 縮小(m_dormant=1 時)了,表示 Client 暫時不需更新畫面,它就不會要求畫面 更新,如下圖紅框。如果 Viewer 沒有被縮小(m_dormant=0)才會發送一個要求 畫面更新的訊息給 Server,亦即,在一個完整的更新完成後,且視窗沒有被縮小 的情況下,Client 才會再傳送一次畫面更新要求訊息。

圖 4-10.觸發畫面更新訊息

在 Client 的解碼過程中需要時間,因此 Server 都會記錄著在這段時間所發生 的變動,當 Client 上一次畫面訊息處理完成並傳送要求畫面更新訊息後,Server 才會將 Client 解碼那段時間,所發生的變動畫面資料一起壓縮送過來,因此,一 直重覆這些動作,Client 的 Viewer 視窗就能持續更新成 Server 最新的畫面。

●2.在 Viewer 視窗最小化還原後

在 Viewer 被 最 小 化 或 還 原 時 , OS 各 自 會 發 送 SC_MINIMISE 、 SC_RESTORE 的訊息,給 Viewer 的訊息處理函數,當 WndProc()收到後會判 斷是何子息,如果是 SC_MINIMISE,它會馬上呼叫 SetDormant()函數,將 m_dormant 狀態會被設為 TRUE,代表現在 Client 被最小化,狀態相當於靜止,

則 Client 不會主動要求畫面更新;當視窗被使用者還原時,WndProc()會收到 SC_RESTORE 的訊息,它馬上會呼叫 SetDormant()函數,會將 m_dormant 設 為 FALSE,而且因為 Viewer 視窗時被最小化的期間不會發送要求畫面更新訊 息,因此目前 Client 的 Framebuffer 是停留在 Server 最後一次更新時候的狀態,

在 Viewer 視窗還原後需要馬上傳送一個畫面更新要求,請 Server 將最後一次更 新之後所有變動的畫面區域資料,用一個更新訊息全部送過來。

圖 4-11.SetDormant()函數

●3 一開始的全螢幕更新

這個情況只會發生一次,即 Client 初始化完成後,因為 Client 的 Framebuffer 尚未有任何畫面資料,因此它需要請 Server 傳送完整的全螢幕畫面資料。

以上就是 Client 可能要求畫面更新的三種機制。

4.3.3 鍵盤 鍵盤 鍵盤 鍵盤、 、 、 、滑鼠動作訊息 滑鼠動作訊息 滑鼠動作訊息 滑鼠動作訊息

這一小節要說明 Viewer 如何將滑鼠和鍵盤的訊息傳送給 Server。下面是 Viewer 視窗對應的訊息處理程式 WndProc 中,當使用者敲擊滑鼠和鍵盤時,OS 都會以訊息的方式傳送到 Viewer 視窗的訊息佇列,而視窗程式的訊息迴圈會從 佇列中取出訊息,呼叫 WndProc()訊息處理函數來對滑鼠或鍵盤敲擊訊息加以處 理。

●滑鼠動作訊息

藍色框框中有對應滑鼠按下左鍵、中鍵、右鍵和移動所產生的訊息種類,當 以上動作產生時,OS 會發送訊息給 Viewer,此時 WndProc 會對他們做處理,

紅色框框是由傳過來的副消息取出滑鼠位置,再呼叫 ProcessPointerEvent()函數

將這些訊息,轉成 Server 看得懂的 RFB Protocol 封包格式傳出去。內部細節只 是一些鍵值轉換,在這地方就不細部說明。

圖 4-12.Mouse Event

下 圖 則 是 敲 擊 鍵 盤 時 訊 息 處 理 , 和 滑 鼠 的 相 似 , OS 也 會 發 送 訊 息 給 WndProc()函數,它則是呼叫 ProcessKeyEvent()函數來轉換他們的鍵值,將這 些鍵值轉成符合 RFB Protocol 格式傳送給 Server。

圖 4-13.KeyEvent

透過 Viewer 視窗的 WndProc()訊息處理函數,將鍵盤滑鼠對 Viewer 動作訊 的息轉成 Server 能夠認知的格式,在 Server 模擬出來,再由 Server 將變動畫面 傳送資料給 Client,如此循環不止,透過以上複雜的機制,一套完整的遠端桌面 程式即可完成。

第五章 第五章 第五章

第五章 Server 和 和 和 和 Client 的溝通訊息 的溝通訊息 的溝通訊息 的溝通訊息

此章統整所有在三、四章中討論過的訊息,將他們細步的封包格式,和 Server、Client 接收後如何處理,在這一節詳細說明。本章依照 RFB Protocol 的 三個 phase,Handshaking Phase、Initialization Phase、Interaction Phase,將 所 有 訊 息 歸 類 成 三 類 , 他 們 各 自 會 在 這 三 個 phase 中 出 現 。 5.1 節 說 明 Handshaking Phase 中,溝通 RFB Protocol Version、安全性種類、密碼認證的 動作和訊息;5.2 節說明在 Initialization Phase 中,初始化的 ClientInit 和 ServerInit 訊息;5.3 節分為兩部分,一、Client 送給 Server 的訊息;二、Server 送給 Client 的訊息;5.4 節則是介紹 VNC 的四種內建的壓縮法的方法:RAW、

CopyRect、RRE、Hextile、ZRLE,和他們的各自的封包格式。

5.1 Handshaking Phase

這些各在 Server 的 vncClientThread Initialization 和 Client 的 Initialization 中完成,在這個 phase,主要是溝通雙方的 RFB Protocol 版本、安全性種類和密 碼認証,這些完成後,代表 Server 准許 Client 進行連線,才會接著後面的 Initialization Phase。

●NegotiateProtocolVersion

一開始會由 Server 傳送 Protocol Version 給 Client,讓 Client 知道 Server 最高可以支援的 RFB Protocol 版本,而 Client 收到後會回傳訊息,告知實際是 用哪一個 protocol!但它不能夠要求高於 Server 的 protocol version。這個機制 是為了使雙方都可以向下支援。目前發布的 RFB 版本有 3.3,3.7,3.8。新增進 的編碼方法和格式,不會改變 protocol 的版本,因為 Server 可以忽略它不懂得 的編碼。

Protocol Version 訊息包含了 12 個 bytes,用一連串的 ASCII 字元解譯成

“RFB xxx .yyy \n”,xxx 和 yyy 是版本前面的字和後面的數字。

表 5-1.Protocol Version No.of Bytes Value

12 “RFB 003.003\n”(hex 52 46 42 20 30 30 33 2e 30 30 33 0a) 12 “RFB 003.007\n”(hex 52 46 42 20 30 30 33 2e 30 30 37 0a) 12 “RFB 003.008\n”(hex 52 46 42 20 30 30 33 2e 30 30 38 0a)

●Authentication

在 Protocol Version 被決定後,Server 必須和 Client 溝通連線安全性的種類。

RFB 版本 3.7 或以上 Server 會先列出它所支援的安全性種類:

表 5-2.Security Message No.of bytes Type Description

1 U8 number-of-security-types

number-of-security-types U8 array security-types

表 5-3.Security-types:

表 5-4.回傳錯誤訊息格式

Server 會列出至少一個支援的有效安全性種類,Client 收到後回傳一個 byte,

表明此連線將使用哪個安全性種類!如果出現問題時,如:LoopBack、Server 未設密碼、或 number-of- security-types= 0 時,這個連線會中斷。之後 Server

Number Name

0 Invalid

1 None

2 NVC Authentication

5 RA2

reason-length U8 array reason-string

會傳送一個 ASCII 的字串描述理由,並在傳回錯誤訊息後馬上關閉連線!

因為 VNC 大部分版本都一定有 None、VNC Authentication 這兩種,因此僅 對此兩個稍做介紹:

1.None:不須認証,且 protocol 的資料以不加密的方式傳送。

2.VNCAuthentication:使用 VNC 認証,protocol 以不加密的方式傳送。Server 會先送一個隨機 16-byte 的 challenge。Client 收到 challenge 後會以 DES 來解碼,

解完後回傳一個 16-byte 的 response。Server 收到 response 會比對是否正確。

在 Security 交握後,Server 會回傳一個 4bytes 的 Security Result 給 Client,

告知 security handshaking 是否成功。不成功,在 3.8 版會傳錯誤訊息告知原因,

而 3.3 和 3.7 版則是直接中斷連線!

表 5-6.Security Result

表 5-6.Security Result