Introduction:準備實踐一些HTML5的新技術來強化項目的工具。設計後台互動部分選擇了HTML5的WebSocket,研究了一下基本的用法,想寫點對於WebSocket實踐的感受。
個人覺得WebSocket的出現是對於Web應用互動性設計的一次革新。WebSocket提出之前,為瞭解決後台推送訊息到前台的需求,提出了一些解決方案,這些方案使用已有的技術(如ajax,iframe,flashplayer,java applet ...),通過一些變通的處理來實現。基本思路都是通過輪詢的方式不斷的由Client
Browser向Server請求任何資料和頁面的變化,亦或通過長串連的方式藉助第三方外掛程式來達到即時的收到Server的資料。
而WebSocket允許後台隨時向前端發送文本或者二進位訊息,WebSocket是一種全新的協議,不屬於http無狀態協議,協議名為"ws",這意味著一個websocket串連地址會是這樣的寫法:ws://localhost:8080/webSocketServer。ws不是http,所以傳統的web伺服器不一定支援,需要伺服器與瀏覽器同時支援,
WebSocket才能正常運行,目前的支援還不普遍,需要特別的web伺服器和現代的瀏覽器。一下是瀏覽器對WebSocket支援情況:
接下來看看基於WebSocket設計的Application常見的架構方式:它的特點是可以支援更多並發串連,並且由於它原理上的雙向性,用戶端的串連可以Net穿透到有防火牆和Proxy的後台Server上。由於WebSocket的通訊協定也非HTML,在新的ws通訊協定設計中,大大減少的傳輸訊息的Size,去掉了傳統HTML
Packet許多冗餘的資訊。瘦身之後的訊息也可以大大提高Web應用的響應效能。
Demonstration:目前對WebSocket支援的Web Server也逐漸多了起來,筆者在實踐的時候選擇的是Jetty 8.1.4在Jetty的lib中包含一個jetty-websocket的JRA包實現了W3C發布的WebSocket介面規範,並且在jetty-util中也包含的基於JSON格式的通訊訊息轉換類。方便開發人員快速的開發WebSocket應用。在後台代碼中主要部分是:1. 繼承並實現WebSocketServlet中的doWebSocketConnection方法2. 實現WebSocket介面中的onOpen, onClose, onMessage等方法
public class AutoAdminServlet extends WebSocketServlet{//private static final long serialVersionUID = 1874288265454885922L;private final Set<AutoAdminSocket> clients;static Logger LOG = Logger.getLogger(AutoAdminServlet.class);public AutoAdminServlet(){clients = new HashSet<AutoAdminSocket>();}@Overridepublic WebSocket doWebSocketConnect(HttpServletRequest req, String message) {LOG.info("Set up a web socket connection: "+message);return new AutoAdminSocket();}class AutoAdminSocket implements WebSocket.OnTextMessage{WebSocket.Connection connection;@Overridepublic void onMessage(String message) {/*Object json = JSON.parse(message);if(!(json instanceof Map))return;@SuppressWarnings("unchecked")Map<String, String> map = (Map<String, String>)json;//TODO*/sendMessage(this, null, "Thanks, I received: "+message);}@Overridepublic void onClose(int code, String message) {LOG.info("Closed and removed a client socket connection.");clients.remove(this);}@Overridepublic void onOpen(Connection conn) {LOG.info("Received a client socket connection.");this.connection = conn;clients.add(this);sendMessage(this, "open", "sample data");}}void sendMessage(AutoAdminSocket client, String action, String message){try{if(message == null || message.isEmpty())message = "n/a";if(action != null)message = "action: "+action+", data: "+message;client.connection.sendMessage(message);}catch(IOException ex){ex.printStackTrace();}}}
前台頁面代碼部分,主要做好以下幾點:1. 在Javascript中判斷瀏覽器是否支援WebSocket,並提供一些友好提示或者備案方案2. 建立WebSocket這個Javascript對象,並謹慎管理它: 忌諱濫用不斷與Server的建立WebSocket,一般一個Browser終端維護一個串連即可,邏輯的多樣性可以通過Command模式來豐富3. 當Browser需要主動與Server通訊時,調用WebSocket API中的send方法4. 當Server主動推送資料到Browser時,在onMessage方法中dispatch多樣的business
var log = function(s) { if (document.readyState !== "complete") { log.buffer.push(s); } else { document.getElementById("output").innerHTML += (s + "\n"); } }log.buffer = []; var socket = null;function init(){window.WebSocket = window.WebSocket || window.MozWebSocket;if(!window.WebSocket){log("WebSocket not supported by this browser");return;}var webSocket = new WebSocket("ws://127.0.0.1:8080/pulsenet/auto");webSocket.onopen = onopen;webSocket.onclose = onclose;webSocket.onmessage = onmessage;socket = webSocket;}function onopen(){log("Open a web socket.");}function onclose(){log("Close a web socket.");}function onmessage(evt){var data = evt.data;if(!data)return;log(data);data = JSON.parse(data);if(!data)return;}function send(){socket.send("Hello web socket server!");}
End在筆者大概體驗了一下WebSocket之後,總結了一些想法:1. WebSocket的後台代碼需要設計健壯而清晰的演算法來判斷哪些訊息是需要推送給哪些用戶端,單個或者多個,或者類似廣播的全部推送,對用戶端的socket connection管理需要考慮安全執行緒和唯一性等問題。2. WebCoekt的前台代碼需要根據business設計簡潔的協議或者命令,還有資料格式,把輪詢的責任轉移到Server端,而在Browser端只專註做好主動請求的邏輯。在接收Server推送和Client主動請求二者操作到頁面共同的部分時,也要謹慎設計頁面資料展示的邏輯和同步問題。
Reference:1. W3C的WebSocket API: http://www.w3.org/TR/2011/WD-websockets-20110419/2. WebSocket一篇很好的介紹:http://websocket.org/aboutwebsocket.html