8小時用HTML5打造VNCViewer


http://cnborn.net/blog/

另一個話題是ThoughtWorks徐昊帶來的《8小時用HTML5打造VNCViewer》。這個分享非常精彩,其實現過程中的思考方式、使用的新技術都讓人有醍醐灌頂的感覺。以下的記錄由現場的筆記總結而來,比較粗略,難免有失誤,還望大家指正。
http://cnborn.net/blog/ 
由於HTML5具備Canvas, WebSocket,所以萌生了使用HTML5來打造一個VNCViewer的想法。同時為這個項目設定目標:在12小時之內完成。

HTML5的定義

在HTML5之前,HTML這個概念僅指代用以描述數據的語意化文檔標簽。之前的W3C始終將HTML定位為單純定義數據的標准,有意淡化BOM(Browser Objective Model)對象。而從HTML5開始,第一次將HTML的概念擴展到HTML+CSS3+JS的集合。在原先的數據表現上添加了一些新的語意化標簽如<header>, <footer>等,但BOM的增強更令人興奮:引入Canvas, WebSQL, WebSocket(在頻繁交互的網絡應用中節約大量資源), PostMessage(在不同頁面之間傳遞數據)等對象為實現更多種應用提供了可能。 

個人項目也要按照標准的項目流程做計划:進行任務分解。有任務分解列表的同時,也要有項目的風險列表。考慮到一些通常的項目風險,比如:一旦協議太復雜以致於不能用很短的時間了解,就會影響項目實現。

首先需要了解VNC協議,任務預計需要兩小時。發現VNC的工作原理並不復雜:服務器和客戶端經過握手確定協議版本、所支持的編碼方式等,隨后開始通信,傳輸屏幕上的顯示內容。顯示內容傳輸時支持不同編碼方式,協議本身可以擴展以支持更多種編碼方式。VNC的協議有43頁(鏈接),1小時閱讀完畢。其中主要包括兩大部分,顯示和接受輸入。出於應用需要,不考慮輸入部分的實現。

此時任務列表更新為:

  • 建立連接
  • 服務器與客戶端間進行握手
  • 開始傳送數據

使用HTML5的WebSocket建立連接時,發現WebSocket要求需要HTTP協議才能建立連接;同時建立長連接還需要如下步驟:HTML5端會發送一個請求,詢問服務器是否能將協議升級成為WS/WSS協議,服務器需回復確認。但VNC服務器誕生較早,不支持升級協議這個約定。有兩種解決方法:自己實現一個VNC Server,或者寫一個Proxy來解決問題。因為自己實現VNC Server成本太高,不可能在時間限制內完成,所以選擇了寫Proxy的方案。
Proxy使用node.js , 一個運行在服務器端的JavaScript框架來完成。起初選用的原因主要還是個人的興趣,接下來可以看到,最終這個框架拯救了整個項目。這個Proxy只用了10行JavaScript,使服務器和客戶端的兩個TCP流對接上即可。

服務器端代理部分耗時45分鍾

接下來面臨的是編碼問題,VNC使用底層數據編碼,而HTML端是相對高層的數據編碼方式,這里通過node.js實現統一;服務器建立連接需要認證,VNC的認證機制使用DES加密。在網上尋找JavaScript DES庫的時候,發現能找到的三個庫均不能正常工作。不得已自己實現了JavaScript的DES庫,耗費了不少時間。此時5個小時過去了,服務器端和客戶端已經可以正確連接。

接下來解決顯示的問題

Canvas有一個繪制函數幾乎可以原生支持VNC的Raw編碼方式,於是直接使用這個方法實現。測試時發現基本不能正常使用:由於數據傳輸量非常大,客戶端的性能完全不能滿足需求,畫圖速度太慢,占用資源過高。

6個小時過去了

考慮在信息傳輸方式上做優化,傳遞每個像素數據的Raw編碼方式所需數據量過大。同時實驗中發現不同VNC服務器發送信息的行為不太一樣:蘋果的服務器按照行的方式發送屏幕顯示數據,而某個版本Linux中則是直接把屏幕分為四個區域來處理顯示更新。按照區塊刷新的編碼方式進行了測試,發現並不能解決問題:畫面后面的幀顯示比原先略快但仍不可用,並且顯示第一幀畫面的速度非常慢。

解決傳輸數據量的問題,需要從傳輸協議上入手。VNC協議默認有5種Encode方式,分別是:

  • 全屏更新
  • 區域刷新
  • Hextile(將屏幕分成16x16的諸多小塊來進行刷新,詳解
  • zlib 將raw的數據進行壓縮然后再傳輸
  • hextile+zlib,將Hextile格式的數據進行壓縮再傳輸

參考一些資料,均推薦使用zlib方式對數據進行壓縮處理,可以節省帶寬、提高速度(未經壓縮的畫面一幀的流量是4.3M)。此時需要一個JavaScript的zlib實現來進行解碼工作。發現沒有這樣的庫...... 此路不通。

能否使用HTML5的Worker進行后期處理?查閱文檔發現Worker進程不能直接訪問DOM對象,所以不能在Canvas上面進行繪畫。而且傳遞大數據量時速度很慢。簡單地說這個功能適用於計算密集的任務,但不適合這種數據密集的任務。

最后解決問題的關鍵功能,是一個比較陳舊、平時幾乎不再使用的瀏覽器功能 - DataURI Encoding。即把資源經由Base64編碼后直接顯示在頁面中。這里面最重要的突破在於:從最終目的中思考,用戶最終的目的是什么?所需的VNC解碼內容和哪些瀏覽器支持的原生信息格式最為接近?
首先想到的答案是視頻,但是發現如果使用HTML5的<video>標簽需要把VNC流轉換為視頻格式。這個工作太復雜,幾乎無法完成。
如果不能作為視頻來處理的話,那么作為圖片顯示的方式是否可行呢?把VNC的數據流轉化為圖片,瀏覽器即可通過硬件加速來顯示圖片。將VNC流轉換成相應的圖片格式在客戶端進行太復雜,同時非常消耗資源。這時之前在服務器端選用的node.js技術發揮了重要作用。在VNC服務器端編寫了一個新的VNC編碼方式,可以直接將VNC的數據流以JPEG的方式進行編碼(解決了傳輸數據量的問題),然后在服務器的node.js端對數據流進行解碼,直接向瀏覽器傳回通過Base64編碼的JPEG圖片,即可做到以很低的延遲顯示VNC服務器的內容。

至此,整個項目完成,共耗時8小時23分鍾


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM