大漠落日

while(!dead) study++;
posts - 46, comments - 126, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

RFB--VNC的基础(转载)

Posted on 2009-11-17 08:53 乱78糟 阅读(1656) 评论(0)  编辑 收藏 引用 所属分类: 重要资料
RFB (Remote Framebuffer)
為一種簡單的遠端存取協定,讓使用者以圖像的方式存取遠端電腦。此協定運作於Framebuffer層級,因此能夠應用於所有的視窗介面,其包括X11、Windows及Macintosh。VNC即是RFB協定一種應用。
要求連線,並得到遠端畫面的一方稱之為Client或是Viwer,對相的另一端稱之為RFB Server。RFB實為一種「Thin-Client」的協定,此重點在於建立一個功能上需求極精簡的Client,在這方面,Client可以應用於更廣泛的硬體上,而Client的功能愈簡單愈好。  RFB協定同時使得Client成為一種「stateless」的形態,假若Client中斷連結後,隨及再度連結同個Server,Client端的畫面將會保留。此外,不同的Client允許對同樣的RFB Server做連結。在新的端點上的使用者會看到與Server主機相同的畫面,實際上,這樣的介面讓使用者更能夠任意的改變位置。不論如何,只要得到合 適的網路連線,使用者就能夠任意存取個人應用軟體,這些應用軟體能夠在兩地之間做到存取的動作,不過何處,都能提供使用者熟悉的、客製化的視覺基礎。

RFB內容還包括了以下各式協定:

Display Protocol
顯示協定的基礎圍繞在原始單一圖像(single graphics primitive):
  『將某區塊的像素資料,設定至(x,y)位置上』
在 最初看來,以這種方式繪出使用者介面似乎會是個無效率的方法。然而,允許多種不同的編碼方式,對於像素資料處理中,交換參數值方面得到了相當大的使用彈 性,例如頻寬、Client繪圖速度、Server 處理速度等。  給定一串序列的資料可對Framebuffer做更新的動作。而每一次的更新,都會將舊有的Framebuffer更動至另一種狀態,這 方面有點類似一段影片中的影格(Frame),基本上每個區塊都不會相交,但還是會有不一定的狀況出現。  更新協定為一種「需求導向」的協定,意思是說 「在Client要求更新時,訊息只會由Server發送」,這方式使得此協定能有最合適的通訊品質,慢速Client會更新的較慢。一般應用程式在 Framebuffer相同區段改變時,會變換的較其他區段來的快速,若是在傳輸緩慢的網路中,Framebuffer的更新可能會被乎略掉。

Input protocol
輸入以標準工作工如鍵盤、多按鈕裝置為主。Client在任意地點,按下按鍵或是點座標裝置移動時,都會對Server發送要求訊息。這些被輸入的訊息亦可由非標準輸入裝置發送,整合為可被Server接受的編碼。

Display pixel data
RFB在 開始送傳資料前,Client及Server 會對將被傳送的像素資料的格式及編碼方式做協調,這部份的協調是為了讓Client端的工作愈簡單愈好,Server必需提供Client最基本的像素資 料的需求,假若 Client本身能夠應付其他多種編碼方式,Server則會選擇最簡單易行的編碼方式來做傳送的動作。  像素資料包函個別顏色內容,最普遍的顏色格式 如「真彩」的24-bit及16-bit,每個bit區段直接轉為RGB對應的強度色彩。  「編碼」(encoding)會影響區段內的像素資料如何傳 送,每個像素資料區塊都預先被指定一個(x,y)螢幕位置,而區塊的長、寬、編碼都將被指定在像素資料的資料區塊中,而區塊資料也會依據指定的編碼方式編 碼。

Extension protocol
在此有多種方法能夠擴展協定:
新增一個編碼的型態對於現有的Client及Server來說是相對上的容易做到,只不過Server會自動乎略它不支援的新編碼型態,而Client也不會去求要一個新的編碼方式,因此不會產生這種編碼方式的資料區塊。

編 碼資料中對於偽編碼(Pseudo Encodings),Client可以對Server要求“pesudoencoding”,宣告其本身支援這一類型的擴充協定;假若Server不支 援”pseudo-encoding”則會略乎此編碼。該注意的是Client必需假設Server不支援這類的擴充,直到Client從Server取 得真正完成擴充認證。

新的安全型態
協定在新新增一個安全型態時有很大的彈性,而不必擔心Client及Server的相容性問題。當Client及Server 都這同了新的安全型態後,雙方在資料交換方面將更加有效率,並不見得每一項工作都需依照RFB協定。

在任何情況下使用不同版本的協定
RFB協定的版本主要被維護人員定訂,假若你的協定與Server使用了不正確的版本號碼,雙方將無法相容。最重要的一點在於確認RFB協定的相容性,以確保編碼及安全型態不會發生衝突。


協定訊息
RFB協定可以依任何可靠的定協傳送資料 (如TCP/IP),即使byte-streamor。正常來說RFB主要透過TCP/IP達成連線。完成一個RFB協 定有三個步驟:第一步為「handshaking phase」,此階段主要是去同意協定的版本及使用的安全型態;第二步為「initialization phase」,即對Server與Client互相交換「ClientInit」及「ServerInit」的訊息。第三步為「normal protocol interaction」,Client將能夠傳送任何它想傳送的訊息,然後接受Server回傳的結果。

協定版本
「Handshaking Phase」開始前,Server會傳送ProtocolVerstion的訊息給Client,使得Client能夠知道Server端支援RFB的最高版本,隨而Client回應Server可使用的版本號碼(不一定與Server的版本號碼相同),正常來說,Client不該要求高於Server端RFB的版本。

安全
一 定協定版本確認後,Client、Server在連線時,必需協議共同的安全性標準。假若Client支援的安全性協定有在Server列舉的安全性協定 清單中,Client會回傳單一byte的訊息告知Server某安全性協定可被用於這次的連結。  在進行Handshaking Phase時,不論這一階段的連成是否成立,Server都會傳送一個字串通知Client,假若連線成立,RFB協定即進行下一階段──Initialization Phase.
假若安全性連線失敗,Server則傳送一則字串描述此異常,隨後終止連結。

VNC認證
連線時,隨送傳送16位元的VNC認證,而協定資料不做加密的動作。Server會隨機產生一個16 byte的要求Client回應特定的訊息。Client依據Server傳送過來的16 byte編碼為金鑰,並加密回傳此金鑰以回應Server。而此更協定依據項些安全性協定的回應持續進行下去。

訊息初始化
當Client、Server建立安全性連結後,接著會進行初始化層段(initialsation phase)
這個階段,在Client傳送一個「ClientInit」,Server隨即會傳送「ServerInit」訊息。

ClientInit:
Client 回應的訊息只有一個「shared-flag」參數,若值非「0」(true),則表式Server試著去持續共享桌面而不對其他的Client端做終止 連線的動作,反之,若值為「0」(false),則表示終止其他連線,僅對某Client端提供桌面分享的服務。

ServerInit:
Server 接受ClientInit訊息後,會接著發送ServerInit訊息,此訊息會告知Client端Framebuffer的長、寬、像素格式以及此桌面相關名稱。

Client給Server的訊息內容
由Client所發出的訊息形態如下:
SetPixelFormat
SetEncodings
FramebufferUpdateRequest
KeyEvent
PointerEvent
ClientCutText
此外,其他仍有需要另行定義的訊息形態,需注意的是假如一則訊息形態沒有事先定義在Client的文件中,Client需確定Server傳送此特定的訊息形態後,Client仍能夠有效的支援。

Client端訊息
SetPixelFormat
設定像素資料後,接會會被置入Framebuffer中的FramebufferUpdate,假若Client沒有送出SetPixelFormat資料,Server在送出像素資料時將會依據即定的像素格式(格式請參考文件6.3.2)。
SetEncodings
指 定可被Server接受的編碼格式。而設定於送給Server訊息中的編碼格式將會是雙方做連結時,編碼格式的首選,但此編碼不一定會被Server選 擇。即使沒有明確指定,像素資料都會以row的方式被傳送。此外,Client可對原始編碼資料做「偽編碼」(pseudo-encodings)的要 求,以告知Server本地端支援此類型的擴充,假若Server不支援此「偽編碼」的類型則會直接乎略不使用。需注意的是,Client必需假設 Server端不支援Client原始的編碼型態,直到Server認同Client端的原編碼型態後,Client才能夠使用此編碼型態。

FramebufferUpdateRequest
Client 端對Server提出請求,以了解Server端寫入Framebuffer中的x-position、y-position、width、height 數值。Server會以「FramebufferUpdate」對Client端做回應,但需注意的是,多個 FramebufferUpdateRequest可能只會由單一一個FrameBufferUpdate回應。  Server會假設Client不斷 的截取存放在Framebuffer中Client所需求的部份(x-position,y-position,width,height),因此 Server僅需要對Client傳送不斷更新的部份。  但在某些狀況下,Client遺失某特別需求的部份,Client將傳送一則 「FramebufferUpdateRequest」訊息,其中參數incremental幫需設為「0」(false),這會對Server請求傳送 某指定的Framebuffer的全部內容。  假若Client沒有遺失Framebuffer中任何必要的部份,則傳送 FramebufferUpdateRequest時incremental的內容值將會是非0值(true),假若Framebuffer的內容有所更 新,Server亦會傳送一個FramebufferUpdate訊息。這裡需注意的是「雙方發某更新訊息的時間是不固定的」。  在快速網路的狀況 下,Client會去範規更新傳送的速度以避免獨佔頻寬。

KeyEvent
假若鍵盤按鍵按下,Framebuffer中 Down-flag參數值將會是非0值(true),反之則否。其中由X Window所定義的「keysym」值,被指定用於KeyEvent,作為所謂的按鍵「key」。一般來說,這裡的keysym的key值等同於 ASCII所訂定的內容值。keysyms並不容易了解,為了提高keysyms能被廣泛的利用,需要注意以下事項:
1、「shift」的狀態 (不論shift是否被按下),在keysym動作時應該被視為「背景執行」。舉例來說,要按下US鍵盤的「#」需要按住「shite + 3」,但UK鍵盤則不需按下shift。假若server端為US鍵盤,想要從使用UK鍵盤的Client接收一個「#」號,Client將不會有 shift被按下的動作。在這狀況下,Server為了取得一個「#」而非數字「3」,將會內部將shift設為「已按下」。
2、在keysym 中,大小寫的區別常非重要。這不同於某些鍵盤於X Window下將大小寫視為相同的狀況。例如,Server接收keysym值中的大寫「A」時,即使沒有按下shift也該認為是大寫的「A」,這代表 的是此時,server內部需假定已按下了shift。
3、Server應該呼略keysyms中的「lcok」,如CapsLock和NumLock,且應該依據不同的狀況來解釋每一個接收到的keysyms字元。
4、不同於shift的其他修飾鍵如「Ctrl」、「Alt」等,一樣需要用來反應keysyms值,要注意的是在keysyms中沒有如「ctrl+a」之類的值,這些值(ctrl、alert)只能在Client按下a後跟著被傳送出去。
5、 以Client端來說,當他們按下Ctrl或Alt時同樣會產生keysyms中的某些字元,而Client可能需要另外傳送「Release」事件以確 保傳送的keysym值能夠正確被Server了解。例如,在German PC鍵盤上,「Ctrl+Alt+q」會產生「@」,在這狀況下,Client需要為「Ctrl」或「Alt」傳送一個假的「release Ctrl」、「release Alt」訊息給 Server以確保keysym值正確被Server解釋。
6、在X Window裡,「backword tab」並沒有通用的標準,在某些系統裡,shift+tab會產生「ISO Left Tabe」的keysym值,其他系統則可能產生「Back Tab」或僅僅產生「Tab」值,或是由應用程式從shift的狀態來判別「backword-tab」而不是「forward-tab」。在RFB協定採用最後一種方式,Client應該產生shifted Tab而不是ISO Left Tab。然而,為後符合backward-compatible的Client,Server應該懂得判別ISO Left Tab同於另一種shift Tab。
PointerEvent
標 示如「點的移動」或「點按鈕的按、放」等。點的位置設定於(x-position,y-position),而目前1~8的按鈕狀態由bits0~7的按 鈕遮罩所表示,0代表放開,1代表按下。以滑鼠來說,按鈕1、2、3分別對應至左鍵、中鍵、右鍵等。以有滾輪的滑鼠來說,每一個「向前滾動」的動作都代表 按下、放開「按鈕4」,向後滾動則以「按鈕5」來表示。

ClientCutText

Server訊息


Server to client messages
Server傳送給Client的訊息包函在下列變數中:
FramebufferUpdate
SetColourMapEntries
Bell
ServerCutText

需注意的是在傳送一則沒有定義於Server文件中的訊息前,Server需要從Client取得「extension specific」,以確定Client在接收後能夠支援此編碼,對此,通常會對Client要求一個偽編碼(pseudo-encoding)。

FramebufferUpdate
一 個FramebufferUpdate的內容主要包函一序列的像素矩陣資料(Client必需將這些像素資料填入Client端的 Framebuffer),而這一則命令主要在Client發出FramebufferUpdateRequest後產生,需注意的事Update及 UpdateRequest的發送時間並沒有明確的定義。

SetColourMapEntries
像素格式若使用「colour map」時,訊息將會告訴Client端其指定的像素值必需對應至指定的RGB強度。

Bell
觸發Client端的Bell物件(若Client有此物件)

ServerCutText

編碼
文件中所定義的編碼型態包括:
Raw
CopyRect
RRE
Hextile
ZRLE
Cursor pseudo-encoding
DesktopSize pseudo-encoding

各編碼內容:
Raw encoding
此為最簡單的編碼型態。此編碼主要包括width x height像素值(Server端產生的四方體,其長、寬的值),這兩項變數值表示每畫面的像素值,並由左至右做線性排列。所有的RFB Client必需能夠去處理Raw編碼中的像素資料。而RFB Server也應該只以Raw編碼為編碼方式,除非Client要求其他的編碼方式。

CopyRect encoding
CopyRect(copy rectangle)編碼屬於簡單且有效率的編碼方式,能夠於Client在Framebuffer中有相同區塊的像素資料中使用。這項編碼方式會先在填 入Rect的X,Y標,這動作將使Framebuffer得到一個位置,隨後Client可以取得Rect的像素資料。這項編碼可以用於Client畫面 不斷變動狀況,當使用者移動某視窗,或是捲動視窗捲軸時,更能明顯看出此編碼的效果,但若用於對視窗最佳化、重繪圖像等動作時,就不會有明顯畫面變化的效 果。Server對圖像只做一次最正確的傳送是最正確的作法,接著要知道這個圖像先前於Framebuffer的位置(x,y),最後使用 CopyRect編碼傳送更新的Framebuffer敘述(subsequent)。

RRE encoding
RRE指 rise-and-run-length,其基礎在於模擬一段的二維編碼。RRE編碼所包函的rectangle會以能夠被簡易的繪圖engines有效 率而快速繪製的形式到達Client端。  RRE編碼的缺點在於無法適用於複雜的桌面,但仍能使用於某些狀況。RRE編碼的基礎在於分割某像素資料的 rectangle成多個副屬的rectangle,每一個副屬的rectangle都包括了各別區塊的像素內容,而將這些副屬的rectangle組合 後,將會得到原始的rectangel,這些最適的副屬區塊相對的容易被計算出來。這項編碼包括背景像素值,



Hextile encoding
Hextile 編碼為RRE的一種延伸,其將rectangle分成16 x 16的區塊,可令其大小為每邊區塊4 bit,而一個被化分出來的rectangle共16bit。每個rectangle由左上開始往右下計算,先左而右,由上至下,而編碼的內容都會依據先 前的rectangel排列模式做排列。假若一個rectangle的寬度並非16的倍數,則最後一個rectangel在劃份後的寬度將會小於16。高 度的原理同於寬度。  每個區塊不管以Raw編碼或是RRE編碼都會有背景像素值(background pixel),若背景像素的數值同於前一區塊,則不需特別設定至新的區塊中。另一個狀況:若傳送資料是以Raw編碼,背景像素值則不需要做任何設定。假若 所有的副屬區塊(subretangle)都擁有相同的背景像素值,可當做整個區塊的前景顏色像素值,僅做一次性的設定;前景像素可同於背景像素的指定方 式,其不需特別設定至區塊中,可從前一個區塊中取得。假若先前的區塊是Raw或是SubrectColored的設定,當下的區塊就不會特別前景像素設定 至當下的區塊中,但會以AnySubrects從前一區塊取得前景像素(假定前一區塊已經從更前面的區塊取得前景像素值)。

ZRLE encoding
ZRLE代表的是Zlib1 Run-Length Encoding,擁有Z-lib的壓縮、劃分區塊的能力。在傳送的序列資料中,每個區塊由4 byte長的標頭開始,隨後接著由z-lib壓縮的資料。每個單z-lib序列物件主都會在RFB協 定的連結中產生作用,所以編碼、解碼需定訂嚴格的順序。  Z-Lib的資料在解壓後,會產生64 x 64的區塊,其值由左至右、由上至下排列(同於Hextile),若資料的長寬bit數不是64的倍數,則最後一個區塊的長寬將會小於64 bit。  ZRLE利用CPIXEL(compressed pixel)技術,這方法同於一般的像素格式,其中所有的bit由(r,g,b)構成,不管(r,g,b)強度為何,true-colour-flag的 值不會是0,bitperpixel的值是32,depth值會在24以下。ZRLE中,一個CPIXEL只會有3 byte的長度,亦可能包函最低顯著或最高顯著的(r,g,b)值。bytePerCPixel為ZRLE中CPIXEL的bytes數。  每個區塊以 subbencoding type byte開始,假若區塊編碼的起始byte有做過任何設定,則代表此區塊已運行過或是已被清除等等。區塊最底部的7個位元指定著色的範圍,0代表沒有做任 何著色;1代表區塊使用單一色系,2~127表示區塊共使用了多少的顏色。

Pesudo-encodings
Cursor pseudo-encoding
Client 若對Server發送這一則指令,即表示Client將有能力在本地端劃出server mouse的位置,這能夠在慢速的連線中明顯提升畫面呈現的效果。Server傳送FreamebufferUpdate時,會同時在Cursor pseudo-encoding設定pesudo-rectangle的內容回應Client的更新要求。pesudo-rectangle的內容包函 x-position及y-position,其指出server端Cursor的位置,而width、height分別為Cursor的像素,並依據 bitmask來設定資料的內容(所謂的bitmask,其內容主要是由左向右、由上至下對像素值做線性排列。

DesktopSize pseudo-encoding
Client 端對Server請求DesktopSize pseudo-encoding以告知本地端可應付Framebuffer中width、height值變動的狀況。Server通常會將這一個 pseudo-rectangle設定至最後一個Framebuffer資料的DesktopSize pseudo-encoding中,而此項目中pesudo-rectangle的x-position及y-position會被乎略,width及 height值會告知新的Framebuffer大小。

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理