WebSocket(二)

HTML5 WebSockets是HTML5中最強大的通信功能,它定義了一個全雙工通信信道,僅通過Web上的一個Socket即可進行通信。

目前實時Web應用的實現方式,大部分是圍繞輪詢和其他服務器端推送技術展開的,Comet、輪詢、長輪詢、流(streaming)解決方案,所有這些提供實時數據的方式包含有大量額外的、不必要的報頭數據,會造成傳輸延遲。最重要的是爲了在半雙工HTTP的基礎上模擬全雙工通信,目前的許多解決方案都是使用了兩個連接:一個用於下行數據流,另一個用於上行數據流。這兩個連接的保持和協作也會造成大量的資源消耗,並增加了複雜度。

WebSockets就是解決以上問題的方案。爲了建立WebSocket通信,客戶端和服務器在初始握手時,將HTTP協議升級到WebSocket協議。

 

現在WebSocket服務器有很多,還在開發中的更多。有一下幾種:

  • Kaazing WebSocket Gateway:一種基於Java的WebSocket網關。
  • mod_pywebsocket:一種基於Python的Apache HTTP服務器擴展。
  • Netty:一種包含WebSocket的Java框架。
  • node.js:一種驅動多個WebSocket服務器的服務器端JavaScript框架。
對於非原生支持WebSocket的瀏覽器來說,Kazzing的WebSocket網關包含了完整的客戶端瀏覽器WebSocket模擬支持。

二、HTML5 WebSockets API

1、瀏覽器支持情況檢測

檢測瀏覽器支持情況代碼  收藏代碼
  1. function loadDemo() {  
  2.     if (window.WebSocket) {  
  3.         //supported  
  4.     } else {  
  5.         // not supported  
  6.     }  
  7. }  

2、WebSocket對象的創建和服務器連接

要連接通信端點,只需要創建一個新的WebSocket實例,並提供希望連接的對端URL。ws://和wss://前綴分別表示WebSocket連接和安全的WebSocket連接。

Js代碼  收藏代碼
  1. url = "ws://localhost:8080/echo";  
  2. w = new WebSocket(url);  

 

建立WebSocket連接時,可以列出Web應用能夠使用的協議。WebSocket構造函數的第二個參數既可以是字符串,也可以是字符串組。

Js代碼  收藏代碼
  1. w = new WebSocket(url, ["proto1""proto2"]);  

假設proto1和proto2是定義明確、可能已註冊且標準化的協議名稱,它們能夠同時爲客戶端和服務器端所理解。服務器會從列表中選擇首選協議。

Js代碼  收藏代碼
  1. onopen = function(e) {  
  2.     //確定服務器選擇的協議  
  3.     log(e.target.protocol);  
  4. }  

 

3、添加事件監聽器

WebSocket編程遵循異步編程模型;打開socket後,只需等待事件發生,而不需要主動向服務器輪詢,所以需要在WebSocket對象中添加回調函數來監聽事件。

WebSocket對象有三個事件:open、close和message。

Js代碼  收藏代碼
  1. w.onopen = function() {  
  2.     log("open");  
  3.     w.send("send message");  
  4. }  
  5. w.onmessage = function(e) {  
  6.     log(e.data);  
  7. }  
  8. w.onclose = function(e) {  
  9.     log("closed");  
  10. }  
  11. w.onerror = function(e) {  
  12.     log("error");  
  13. }  

 

4、發送消息

當socket處於打開狀態(即onopen之後,onclose之前),可以用send方法來發送消息。消息發送完,可以調用close方法來終止連接,也可以不這麼做,讓其保持打開狀態。

Js代碼  收藏代碼
  1. w.send();  

 

你可能想測算在調用Send()函數之前,有多少數據備份在發送緩衝區中。bufferAmount屬性表示已在WebSocket上發送但尚未寫入網絡的字節數。它對於調節發送速率很有用。

Js代碼  收藏代碼
  1. document.getElementById("sendButton").onclick = function() {  
  2.     if (w.bufferedAmount < bufferThreshold) {  
  3.         w.send(document.getElementById("inputMessage").value);  
  4.     }  
  5. }  

WebSocket API支持以二進制數據的形式發送Blob和ArrayBuffer實例

Js代碼  收藏代碼
  1. var a = new Uint8Array([8, 6, 7, 5, 3, 0, 9]);  
  2. w.send(a.buffer);  

 

三、例子

書中介紹了一個用Python寫的Echo服務,書中的代碼可以在http://www.apress.com/9781430238645 的“Source Code/Downloads”中下載下載。

對於JAVA開發人員Tomcat是最熟悉的,在Tomcat8中已經實現了WebSocket API 1.0。Tomcat7也會在不久實現(現在的實現不是WebSocket API 1.0)。

在這裏寫一下在Tomcat8下執行的例子。

例子由客戶端頁面和WebSocket服務程序組成,功能在Echo基礎上增加一個服務端定時發送信息的功能。

 

頁面程序 ws.html代碼  收藏代碼
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4. <meta charset="UTF-8">  
  5. <title>Test WebSocket</title>  
  6. <script type="text/javascript">  
  7.     //顯示信息  
  8.     var log = function(s) {  
  9.         if (document.readyState !== "complete") {  
  10.             log.buffer.push(s);  
  11.         } else {  
  12.             document.getElementById("output").textContent += (s + "\n");  
  13.             document.getElementById("outputdiv").scrollTop = document.getElementById("outputdiv").scrollHeight;  
  14.         }  
  15.     }  
  16.     log.buffer = [];  
  17.     //顯示連接狀態  
  18.     function setConnected(status) {  
  19.         document.getElementById("socketstatus").innerHTML = status;  
  20.     }  
  21.     var ws = null;  
  22.       
  23.     //連接  
  24.     function connect() {  
  25.         if (ws != null) {  
  26.             log("現已連接");  
  27.             return ;  
  28.         }  
  29.         url = "ws://localhost:8080/websocket/mywebsocket";  
  30.         if ('WebSocket' in window) {  
  31.             ws = new WebSocket(url);  
  32.         } else if ('MozWebSocket' in window) {  
  33.             ws = new MozWebSocket(url);  
  34.         } else {  
  35.             alert("您的瀏覽器不支持WebSocket。");  
  36.             return ;  
  37.         }  
  38.         ws.onopen = function() {  
  39.             log("open");  
  40.             setConnected("已連接");  
  41.             //設置發信息送類型爲:ArrayBuffer  
  42.             ws.binaryType = "arraybuffer";  
  43.               
  44.             //發送一個字符串和一個二進制信息  
  45.             ws.send("thank you for accepting this WebSocket request");  
  46.             var a = new Uint8Array([8675309]);  
  47.             ws.send(a.buffer);  
  48.         }  
  49.         ws.onmessage = function(e) {  
  50.             log(e.data.toString());  
  51.         }  
  52.         ws.onclose = function(e) {  
  53.             log("closed");  
  54.         }  
  55.         ws.onerror = function(e) {  
  56.             log("error");  
  57.         }  
  58.     }  
  59.       
  60.     //斷開連接  
  61.     function disconnect() {  
  62.         if (ws != null) {  
  63.             ws.close();  
  64.             ws = null;  
  65.             setConnected("已斷開");  
  66.         }  
  67.     }  
  68.       
  69.     window.onload = function() {  
  70.         connect();  
  71.         log(log.buffer.join("\n"));  
  72.         //發送頁面上輸入框的信息  
  73.         document.getElementById("sendButton").onclick = function() {  
  74.             if (ws != null) {  
  75.                 ws.send(document.getElementById("inputMessage").value);   
  76.             }  
  77.         }  
  78.         //停止心跳信息  
  79.         document.getElementById("stopButton").onclick = function() {  
  80.             if (ws != null) {  
  81.                 var a = new Uint8Array([19201516]);  
  82.                 ws.send(a.buffer);   
  83.             }  
  84.         }  
  85.     }  
  86. </script>  
  87. </head>  
  88. <body οnunlοad="disconnect();">  
  89.     <div>連接狀態:<span id="socketstatus"></span></div>  
  90.     <div>  
  91.         <input type="text" id="inputMessage" value="Hello, WebSocket!">  
  92.         <button id="sendButton">發送</button><button id="stopButton" style="margin-left:15px">停止心跳信息</button>  
  93.     </div>  
  94.     <div>  
  95.         <button id="connect" οnclick="connect();">連接</button>  
  96.         <button id="disconnect" οnclick="disconnect();">斷開</button>  
  97.     </div>  
  98.     <div style="height:300px; overflow:auto;" id="outputdiv">  
  99.         <pre id="output"></pre>  
  100.     </div>  
  101. </body>  
  102. </html>  

 

服務端程序用註解方式驅動

 

Websocket服務端程序代碼  收藏代碼
  1. package com.test.wsocket;  
  2.   
  3. import java.io.IOException;  
  4. import java.nio.ByteBuffer;  
  5. import java.util.Random;  
  6. import java.util.Timer;  
  7. import java.util.TimerTask;  
  8.   
  9. import javax.websocket.OnClose;  
  10. import javax.websocket.OnMessage;  
  11. import javax.websocket.OnOpen;  
  12. import javax.websocket.PongMessage;  
  13. import javax.websocket.Session;  
  14. import javax.websocket.server.ServerEndpoint;  
  15.   
  16. @ServerEndpoint("/mywebsocket")  
  17. public class MyWebSocket {  
  18.       
  19.     private Session session;  
  20.     private static final Random random = new Random();  
  21.     private Timer timer = null;  
  22.     //停止信息信息指令  
  23.     private static final ByteBuffer stopbuffer  = ByteBuffer.wrap(new byte[]{19201516});  
  24.       
  25.     /**  
  26.      * 打開連接時執行  
  27.      * @param session  
  28.      */  
  29.     @OnOpen  
  30.     public void start(Session session) {  
  31.         this.session = session;  
  32.         try {  
  33.             System.out.println("open");  
  34.             if (session.isOpen()) {  
  35.                 //設置心跳發送信息。每2秒發送一次信息。  
  36.                 timer = new Timer(true);  
  37.                 timer.schedule(task, 10002000);  
  38.             }  
  39.         } catch (Exception e) {  
  40.             try {  
  41.                 session.close();  
  42.             } catch (IOException e1) {}  
  43.         }  
  44.     }  
  45.   
  46.     /**  
  47.      * 接收信息時執行  
  48.      * @param session  
  49.      * @param msg 字符串信息  
  50.      * @param last  
  51.      */  
  52.     @OnMessage  
  53.     public void echoTextMessage(Session session, String msg, boolean last) {  
  54.         try {  
  55.             if (session.isOpen()) {  
  56.                 System.out.println("string:" + msg);  
  57.                 session.getBasicRemote().sendText(msg, last);  
  58.             }  
  59.         } catch (IOException e) {  
  60.             try {  
  61.                 session.close();  
  62.             } catch (IOException e1) {  
  63.                 // Ignore  
  64.             }  
  65.         }  
  66.     }  
  67.   
  68.     /**  
  69.      * 接收信息時執行  
  70.      * @param session  
  71.      * @param bb 二進制數組  
  72.      * @param last  
  73.      */  
  74.     @OnMessage  
  75.     public void echoBinaryMessage(Session session, ByteBuffer bb, boolean last) {  
  76.         try {  
  77.             if (session.isOpen()) {  
  78.                 //如果是停止心跳指令,則停止心跳信息  
  79.                 if (bb.compareTo(stopbuffer) == 0) {  
  80.                     if (timer != null) {  
  81.                         timer.cancel();  
  82.                     }  
  83.                 } else {  
  84.                     session.getBasicRemote().sendBinary(bb, last);  
  85.                 }  
  86.             }  
  87.         } catch (IOException e) {  
  88.             try {  
  89.                 session.close();  
  90.             } catch (IOException e1) {  
  91.                 // Ignore  
  92.             }  
  93.         }  
  94.     }  
  95.       
  96.     /**  
  97.      * 接收pong指令時執行。  
  98.      *  
  99.      * @param pm    Ignored.  
  100.      */  
  101.     @OnMessage  
  102.     public void echoPongMessage(PongMessage pm) {  
  103.         // 無處理  
  104.     }  
  105.       
  106.     @OnClose  
  107.     public void end(Session session) {  
  108.         try {  
  109.             System.out.println("close");  
  110.             if (timer != null) {  
  111.                 timer.cancel();  
  112.             }  
  113.         } catch(Exception e) {  
  114.         }  
  115.     }  
  116.       
  117.     /*  
  118.      * 發送心跳信息  
  119.      */  
  120.     public void sendLong(long param) {  
  121.         try {  
  122.             if (session.isOpen()) {  
  123.                 this.session.getBasicRemote().sendText(String.valueOf(param));  
  124.             }  
  125.         } catch (IOException e) {  
  126.             try {  
  127.                 this.session.close();  
  128.             } catch (IOException e1) {}  
  129.         }  
  130.     }  
  131.       
  132.     /**  
  133.      * 心跳任務。發送隨機數。  
  134.      */  
  135.     TimerTask task = new TimerTask() {  
  136.         public void run() {     
  137.             long param = random.nextInt(100);  
  138.             sendLong(param);  
  139.         }     
  140.     };  
  141.   
  142. }  

 

 

Web.xml代碼  收藏代碼
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">  
  3.   <display-name>websocket</display-name>  
  4.   <welcome-file-list>  
  5.     <welcome-file>index.html</welcome-file>  
  6.   </welcome-file-list>  
  7.     
  8.   <filter>  
  9.         <filter-name>Set Character Encoding</filter-name>  
  10.         <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>  
  11.         <init-param>  
  12.             <param-name>encoding</param-name>  
  13.             <param-value>UTF-8</param-value>  
  14.         </init-param>  
  15.         <init-param>  
  16.             <param-name>ignore</param-name>  
  17.             <param-value>true</param-value>  
  18.         </init-param>  
  19.     </filter>  
  20.       
  21.     <filter-mapping>  
  22.         <filter-name>Set Character Encoding</filter-name>  
  23.         <url-pattern>/*</url-pattern>  
  24.     </filter-mapping>  
  25. </web-app>  

 

將以上程序部署到Tomcat8下,啓動服務。


發佈了35 篇原創文章 · 獲贊 9 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章