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>();
- }
- @Override
- public 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;
- @Override
- public 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);
- }
- @Override
- public void onClose(int code, String message) {
- LOG.info("Closed and removed a client socket connection.");
- clients.remove(this);
- }
- @Override
- public 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/