WebSocket


WebSocket與消息推送

B/S結構的軟件項目中有時客戶端需要實時獲取服務器消息,但默認HTTP協議只支持請求響應模式,這樣做可以簡化Web服務器,減少服務器負擔,加快響應速度,因為服務器不需要與客戶端長時間建立一個通信連接,想要完成實時的消息推送功能還是比較困難的,如聊天室、后台信息提示、實時更新數據等功能,但通過polling(輪詢)、Long polling(長輪詢)、長連接、Flash Socket以及HTML5中定義的WebSocket能完成該功能需要。

一、Socket簡介

Socket又稱"套接字",應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。

​ Socket的英文原義是“孔”或“插座”,作為UNIX的進程通信機制,Socket可以實現應用程序間網絡通信。

img

Socket可以使用TCP/IP協議或UDP協議。

  • TCP/IP協議

​ TCP/IP協議是目前應用最為廣泛的協議,是構成Internet國際互聯網協議的最為基礎的協議,由TCP和IP協議組成:
​ TCP協議:面向連接的、可靠的、基於字節流的傳輸層通信協議,負責數據的可靠性傳輸的問題。

​ IP協議:用於報文交換網絡的一種面向數據的協議,主要負責給每台網絡設備一個網絡地址,保證數據傳輸到正確的目的地。

  • UDP協議

​ UDP特點:無連接、不可靠、基於報文的傳輸層協議,優點是發送后不用管,速度比TCP快。

二、WebSocket簡介與消息推送

B/S架構的系統多使用HTTP協議

HTTP協議的特點:

  1. 無狀態協議
  2. 用於通過Internet 發送請求消息和響應消息
  3. 使用端口接收和發送消息,默認為80端口,底層通信還是使用Socket完成

img

HTTP協議決定了服務器與客戶端之間的連接方式,無法直接實現消息推送,一些變相的解決辦法:

雙向通信與消息推送

  • 輪詢:客戶端定時不斷的向服務器發送Ajax請求,服務器接到請求后馬上返回響應信息並關閉連接。

​ 優點:后端程序編寫比較容易。

​ 缺點:請求中有大半是無用,浪費帶寬和服務器資源。

​ 實例:適於小型應用。

  • 長輪詢:客戶端向服務器發送Ajax請求,服務器接到請求后hold住連接,直到有新消息才返回響應信息並關閉連接,客戶端處理完響應信息后再向服務器發送新的請求。

      優點:在無消息的情況下不會頻繁的請求,耗費服務器器資源較小。
    

​ 缺點:服務器hold連接會消耗資源,返回數據順序無保證,難於管理維護。 Comet異步的ashx

​ 實例:WebQQ、Hi網頁版、Facebook IM。

  • 長連接:在頁面里嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設為對一個長連接的請求或是采用xhr請求,服務器端就能源源不斷地往客戶端輸入數據。 優點:消息即時到達,不發無用請求;管理起來也相對便。 缺點:服務器維護一個長連接會增加開銷。 實例:Gmail聊天

  • Flash Socket:在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript通過調用此Flash程序提供的Socket接口與服務器端的Socket接口進行通信,JavaScript在收到服務器端傳送的信息后控制頁面的顯示。

​ 優點:實現真正的即時通信,而不是偽即時。

​ 缺點:客戶端必須安裝Flash插件;非HTTP協議,無法自動穿越防火牆。

​ 實例:網絡互動游戲。

  • Websocket:WebSocket是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通訊的網絡技術。依靠這種技術可以實現客戶端和服務器端的長連接,雙向實時通信。

​ 特點:事件驅動、異步、使用ws或者wss協議的客戶端socket

​ 能夠實現真正意義上的推送功能

​ 缺點:少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別。

三、WebSocket客戶端

websocket允許通過JavaScript建立與遠程服務器的連接,從而實現客戶端與服務器間雙向的通信。

在websocket中有兩個方法:
1、send() 向遠程服務器發送數據
2、close() 關閉該websocket鏈接
websocket同時還定義了幾個監聽函數    
1、onopen 當網絡連接建立時觸發該事件
2、onerror 當網絡發生錯誤時觸發該事件
3、onclose 當websocket被關閉時觸發該事件
4、onmessage 當websocket接收到服務器發來的消息的時觸發的事件,也是通信中最重要的一個監聽事件。msg.data
websocket還定義了一個readyState屬性,這個屬性可以返回websocket所處的狀態:
1、CONNECTING(0) websocket正嘗試與服務器建立連接
2、OPEN(1) websocket與服務器已經建立連接
3、CLOSING(2) websocket正在關閉與服務器的連接
4、CLOSED(3) websocket已經關閉了與服務器的連接

websocket的url開頭是ws,如果需要ssl加密可以使用wss,當我們調用websocket的構造方法構建一個websocket對象(new WebSocket(url))的之后,就可以進行即時通信了。

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>WebSocket 客戶端</title>
    </head>
    <body>
        <div>
            <input type="button" id="btnConnection" value="連接" />
            <input type="button" id="btnClose" value="關閉" />
            <input type="button" id="btnSend" value="發送" />
        </div>
        <script src="js/jquery-1.11.1.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var socket;
            if(typeof(WebSocket) == "undefined") {
                alert("您的瀏覽器不支持WebSocket");
                return;
            }
            $("#btnConnection").click(function() {
                //實現化WebSocket對象,指定要連接的服務器地址與端口
                socket = new WebSocket("ws://192.168.1.2:8888");
                
                //打開事件
                socket.onopen = function() {
                    alert("Socket 已打開");
                    // socket.send("這是來自客戶端的消息" + location.href + new Date());
                };
                //獲得消息事件
                socket.onmessage = function(msg) {
                    alert(msg.data);
                };
                //關閉事件
                socket.onclose = function() {
                    alert("Socket已關閉");
                };
                //發生了錯誤事件
                socket.onerror = function() {
                    alert("發生了錯誤");
                }
            });
            
            //發送消息
            $("#btnSend").click(function() {
                socket.send("這是來自客戶端的消息" + location.href + new Date());
            });
            
            //關閉
            $("#btnClose").click(function() {
                socket.close();
            });
        </script>
    </body>
</html>

四、WebSocket服務器端

JSR356定義了WebSocket的規范,Tomcat7中實現了該標准。JSR356 的 WebSocket 規范使用 javax.websocket.*的 API,可以將一個普通 Java 對象(POJO)使用 @ServerEndpoint 注釋作為 WebSocket 服務器的端點。

@ServerEndpoint("/push")
 public class EchoEndpoint {

 @OnOpen
 public void onOpen(Session session) throws IOException {
 //以下代碼省略...
 }
 
 @OnMessage
 public String onMessage(String message) {
 //以下代碼省略...
 }

 @Message(maxMessageSize=6)
 public void receiveMessage(String s) {
 //以下代碼省略...
 } 

 @OnError
 public void onError(Throwable t) {
 //以下代碼省略...
 }
 
 @OnClose
 public void onClose(Session session, CloseReason reason) {
 //以下代碼省略...
 } 
 
 }

上面的偽代碼建立了一個WebSocket的服務端。

1、@ServerEndpoint("/push")這個注解是表示將WebSocket服務端運行在ws://[服務器IP或域名]:[Server端口]/項目/push的訪問端點,客戶端瀏覽器已經可以對WebSocket客戶端API發起HTTP長連接了。

2、使用@ServerEndpoint注解的類必須有一個公共的無參數構造函數,@OnMessage注解的Java方法用於服務端接收客戶端傳入的WebSocket信息,這個信息可以是文本格式,也可以是二進制格式。
3、@OnOpen在這個端點一個新的連接建立時被調用。參數提供了連接的另一端的更多細節。Session表明兩個WebSocket端點對話連接的另一端,可以理解為類似HTTPSession的概念。
4、@OnClose在連接被終止時調用。參數closeReason可封裝更多細節,如為什么一個WebSocket連接關閉。

更高級的定制如@Message注解,MaxMessageSize屬性可以被用來定義消息字節最大限制,在下方示例程序中,如果超過6個字節的信息被接收,就報告錯誤和連接關閉。

package action;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

// ws://127.0.0.1:8087/Demo1/ws/張三
// url中的字符張三是的路徑參數,響應請求的方法將自動映射。
@ServerEndpoint("/ws/{user}")
public class WSServer {
    private String currentUser;
    
    // 連接打開時執行
    @OnOpen
    public void onOpen(@PathParam("user") String user, Session session) {
        currentUser = user;
        System.out.println("Connected ... " + session.getId());
    }

    // 收到消息時執行
    @OnMessage
    public String onMessage(String message, Session session) {
        System.out.println(currentUser + ":" + message);
        return currentUser + ":" + message;
    }

    // 連接關閉時執行
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println(String.format("Session %s closed because of %s", session.getId(), closeReason));
    }

    // 連接錯誤時執行
    @OnError
    public void onError(Throwable t) {
        t.printStackTrace();
    }
}

五、測試運行

img

服務器輸出

六、小結與消息推送框架

​ Socket在應用程序間通信被廣泛使用,如果需要兼容低版本的瀏覽器,建議使用反向ajax或長鏈接實現;如果純移動端或不需考慮非現代瀏覽器則可以直接使用websocket。

Flash實現推送消息的方法不建議使用,因為依賴插件且手機端支持不好。

參考博客:WebSocket與消息推送
WebSocket 詳解教程


免責聲明!

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



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