當前位置: 妍妍網 > 碼農

萬字詳解,帶你徹底掌握 WebSocket 用法(至尊典藏版)

2024-04-19碼農

點選關註公眾號,Java幹貨 及時送達 👇

一、 簡介

1.1 什麽是WebSocket

WebSocket是一種協定,用於在Web應用程式和伺服器之間建立即時、雙向的通訊連線。它透過一個單一的TCP連線提供了持久化連線,這使得Web應用程式可以更加即時地傳遞數據。WebSocket協定最初由W3C開發,並於2011年成為標準。

1.2 WebSocket的優勢和劣勢

WebSocket的優勢包括:

  • 即時性: 由於WebSocket的持久化連線,它可以實作即時的數據傳輸,避免了Web應用程式需要不斷地發送請求以獲取最新數據的情況。

  • 雙向通訊: WebSocket協定支持雙向通訊,這意味著伺服器可以主動向客戶端發送數據,而不需要客戶端發送請求。

  • 減少網路負載: 由於WebSocket的持久化連線,它可以減少HTTP請求的數量,從而減少了網路負載。

  • WebSocket的劣勢包括:

  • 需要瀏覽器和伺服器都支持: WebSocket是一種相對新的技術,需要瀏覽器和伺服器都支持。一些舊的瀏覽器和伺服器可能不支持WebSocket。

  • 需要額外的開銷: WebSocket需要在伺服器上維護長時間的連線,這需要額外的開銷,包括記憶體和CPU。

  • 安全問題: 由於WebSocket允許伺服器主動向客戶端發送數據,可能會存在安全問題。伺服器必須保證只向合法的客戶端發送數據。

  • 二、 WebSocket的基本概念

    2.1 WebSocket的協定

    WebSocket 協定是一種基於TCP的協定,用於在客戶端和伺服器之間建立持久連線,並且可以在這個連線上即時地交換數據。WebSocket協定有自己的握手協定,用於建立連線,也有自己的數據傳輸格式。

    當客戶端發送一個 WebSocket 請求時,伺服器將發送一個協定響應以確認請求。在握手期間,客戶端和伺服器將協商使用的協定版本、支持的子協定、支持的擴充套件選項等。一旦握手完成,連線將保持開啟狀態,客戶端和伺服器就可以在連線上即時地傳遞數據。

    WebSocket 協定使用的是雙向數據傳輸,即客戶端和伺服器都可以在任意時間向對方發送數據,而不需要等待對方的請求。它支持二進制數據和文本數據,可以自由地在它們之間進行轉換。

    總之,WebSocket協定是一種可靠的、高效的、雙向的、持久的通訊協定,它適用於需要即時通訊的Web應用程式,如線上遊戲、即時聊天等。

    2.2 WebSocket的生命周期

    WebSocket 生命周期描述了 WebSocket 連線從建立到關閉的過程。一個 WebSocket 連線包含以下四個主要階段:

  • 連線建立階段(Connection Establishment): 在這個階段,客戶端和伺服器之間的 WebSocket 連線被建立。客戶端發送一個 WebSocket 握手請求,伺服器響應一個握手響應,然後連線就被建立了。

  • 連線開放階段(Connection Open): 在這個階段,WebSocket 連線已經建立並開放,客戶端和伺服器可以在連線上互相發送數據。

  • 連線關閉階段(Connection Closing): 在這個階段,一個 WebSocket 連線即將被關閉。它可以被客戶端或伺服器發起,透過發送一個關閉幀來關閉連線。

  • 連線關閉完成階段(Connection Closed): 在這個階段,WebSocket 連線已經完全關閉。客戶端和伺服器之間的任何互動都將無效。

  • 需要註意的是,WebSocket 連線在任何時候都可能關閉,例如網路故障、伺服器崩潰等情況都可能導致連線關閉。因此,需要及時處理 WebSocket 連線關閉的事件,以確保應用程式的可靠性和穩定性。

    下面是一個簡單的 WebSocket 生命周期示意圖:

    在這個示意圖中,客戶端向伺服器發送一個 WebSocket 握手請求,伺服器響應一個握手響應,連線就被建立了。一旦連線建立,客戶端和伺服器就可以在連線上互相發送數據,直到其中一方發送一個關閉幀來關閉連線。在關閉幀被接收後,連線就會被關閉,WebSocket 連線關閉完成。

    2.3 WebSocket的訊息格式

    WebSocket 的訊息格式與 HTTP 請求和響應的訊息格式有所不同。WebSocket 的訊息格式可以是文本或二進制數據,並且 WebSocket 訊息的傳輸是在一個已經建立的連線上進行的,因此不需要再進行 HTTP 請求和響應的握手操作。

    WebSocket 訊息格式由兩個部份組成:訊息頭和訊息體。

    訊息頭包含以下資訊:

  • FIN: 表示這是一條完整的訊息,一般情況下都是1。

  • RSV1、RSV2、RSV3: 暫時沒有使用,一般都是0。

  • Opcode: 表示訊息的型別,包括文本訊息、二進制訊息等。

  • Mask: 表示訊息是否加密。

  • Payload length: 表示訊息體的長度。

  • Masking key: 僅在訊息需要加密時出現,用於對訊息進行解密。

  • 訊息體就是實際傳輸的數據,可以是文本或二進制數據。

    2.4 WebSocket的API

    WebSocket API 是用於在 Web 應用程式中建立和管理 WebSocket 連線的介面集合。WebSocket API 由瀏覽器原生支持,無需使用額外的 JavaScript 庫或框架,可以直接在 JavaScript 中使用。

    下面是一些常用的 WebSocket API:

    WebSocket 建構函式: WebSocket 建構函式用於建立 WebSocket 物件。它接受一個 URL 作為參數,表示要連線的 WebSocket 伺服器的地址。例如:

    let ws = new WebSocket('ws://example.com/ws');

    WebSocket.send() 方法: WebSocket.send() 方法用於向伺服器發送數據。它接受一個參數,表示要發送的數據。數據可以是字串、Blob 物件或 ArrayBuffer 物件。例如:

    ws.send('Hello, server!');

    WebSocket.onopen 事件: WebSocket.onopen 事件在 WebSocket 連線成功建立時觸發。例如:

    ws.onopen = function() {
    console.log('WebSocket 連線已經建立。');
    };

    WebSocket.onmessage 事件: WebSocket.onmessage 事件在接收到伺服器發送的訊息時觸發。它的 event 物件包含一個 data 內容,表示接收到的數據。例如:

    ws.onmessage = function(event) {
    console.log('收到伺服器訊息:', event.data);
    };

    WebSocket.onerror 事件: WebSocket.onerror 事件在 WebSocket 連線出現錯誤時觸發。例如:

    ws.onerror = function(event) {
    console.error('WebSocket 連線出現錯誤:', event);
    };

    WebSocket.onclose 事件: WebSocket.onclose 事件在 WebSocket 連線被關閉時觸發。例如:

    ws.onclose = function() {
    console.log('WebSocket 連線已經關閉。');
    };

    以上是一些常用的 WebSocket API。

    三、 在Java中使用WebSocket

    依賴:

    <dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    </dependency>

    3.1 使用Java WebSocket API編寫WebSocket伺服端

    下面是一個使用 Java WebSocket API 編寫 WebSocket 伺服端的範例程式碼:

    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    @ServerEndpoint("/echo")
    public classEchoServer{
    @OnOpen
    publicvoidonOpen(Session session){
    System.out.println("WebSocket 連線已經建立。");
    }
    @OnMessage
    publicvoidonMessage(String message, Session session)throws IOException {
    System.out.println("收到客戶端訊息:" + message);
    session.getBasicRemote().sendText("伺服器收到訊息:" + message);
    }
    @OnClose
    publicvoidonClose(){
    System.out.println("WebSocket 連線已經關閉。");
    }
    @OnError
    publicvoidonError(Throwable t){
    System.out.println("WebSocket 連線出現錯誤:" + t.getMessage());
    }
    }



    這個範例程式碼定義了一個名為 "echo" 的 WebSocket 端點,它會監聽客戶端發來的訊息,並將收到的訊息返回給客戶端。具體來說,它使用了 @ServerEndpoint 註解來指定 WebSocket 端點的 URL,使用了 @OnOpen @OnMessage @OnClose @OnError 註解來定義 WebSocket 事件處理器。

    要使用這個 WebSocket 伺服端,我們需要部署它到一個支持 WebSocket 的 Web 容器中。例如,我們可以使用 Tomcat 8 或以上版本來執行它。在部署完成後,我們可以使用任何支持 WebSocket 的客戶端來連線這個伺服端,發送訊息並接收伺服器的響應。

    例如,下面是一個簡單的 HTML/JavaScript 客戶端程式碼:

    <!DOCTYPE html>
    <html>
    <head>
    <metacharset="UTF-8">
    <title>WebSocket Demo</title>
    <script>
    var ws = new WebSocket('ws://localhost:8080/echo');
    ws.onopen = function() {
    console.log('WebSocket 連線已經建立。');
    ws.send('Hello, server!');
    };
    ws.onmessage = function(event{
    console.log('收到伺服器訊息:', event.data);
    };
    ws.onerror = function(event{
    console.error('WebSocket 連線出現錯誤:', event);
    };
    ws.onclose = function() {
    console.log('WebSocket 連線已經關閉。');
    };
    </script>
    </head>
    <body>
    <h1>WebSocket Demo</h1>
    </body>
    </html>

    這個客戶端使用了 WebSocket 建構函式來建立一個 WebSocket 物件,並指定連線的 URL 為我們之前部署的伺服端的 URL。它使用了 WebSocket 的事件處理器來處理 WebSocket 事件,例如當 WebSocket 連線成功建立時,它會向伺服器發送一條訊息,並在收到伺服器的響應時打印出訊息內容。

    3.2 使用Java WebSocket API編寫WebSocket客戶端

    下面是一個使用 Java WebSocket API 編寫 WebSocket 客戶端的範例程式碼:

    import javax.websocket.*;
    import java.io.IOException;
    import java.net.URI;
    @ClientEndpoint
    public classEchoClient{
    private Session session;
    @OnOpen
    publicvoidonOpen(Session session){
    System.out.println("WebSocket 連線已經建立。");
    this.session = session;
    }
    @OnMessage
    publicvoidonMessage(String message, Session session){
    System.out.println("收到伺服器訊息:" + message);
    }
    @OnClose
    publicvoidonClose(){
    System.out.println("WebSocket 連線已經關閉。");
    }
    @OnError
    publicvoidonError(Throwable t){
    System.out.println("WebSocket 連線出現錯誤:" + t.getMessage());
    }
    publicvoidconnect(String url)throws Exception {
    WebSocketContainer container = ContainerProvider.getWebSocketContainer();
    container.connectToServer(thisnew URI(url));
    }
    publicvoidsend(String message)throws IOException {
    session.getBasicRemote().sendText(message);
    }
    publicvoidclose()throws IOException {
    session.close();
    }
    }







    3.3 使用Spring Boot編寫WebSocket伺服端

    建立Spring Boot計畫

    首先,您需要建立一個新的Spring Boot計畫。可以使用Spring Initializr建立一個新計畫,添加依賴項。

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

    配置WebSocket

    應用程式中,需要配置WebSocket。建立一個新的Java類,並添加註釋 @ServerEndpoint("/websocket") 。這將指定WebSocket伺服端的端點。

    在此類中,需要實作幾個方法:

    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    @ServerEndpoint("/websocket")
    public classWebSocketServer{
    @OnOpen
    publicvoidonOpen(Session session){
    System.out.println("Connection opened: " + session.getId());
    sessions.add(session);
    }
    @OnMessage
    publicvoidonMessage(Session session, String message)throws IOException {
    System.out.println("Received message: " + message);
    session.getBasicRemote().sendText("Server received: " + message);
    }
    @OnClose
    publicvoidonClose(Session session){
    System.out.println("Connection closed: " + session.getId());
    sessions.remove(session);
    }
    privatestaticfinal Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
    }



    處理WebSocket訊息

    @OnMessage 方法中,可以處理WebSocket客戶端發送的訊息,並向客戶端發送響應。下面是一個簡單的範例程式碼:

    @OnMessage
    publicvoidonMessage(Session session, String message)throws IOException {
    System.out.println("Received message: " + message);
    session.getBasicRemote().sendText("Server received: " + message);
    }

    在此程式碼中,我們簡單地打印出收到的訊息,並向客戶端發送響應。

    關閉WebSocket連線

    @OnClose 方法中,可以刪除連線並做一些清理工作。下面是一個範例程式碼:

    @OnClose
    publicvoidonClose(Session session){
    System.out.println("Connection closed: " + session.getId());
    sessions.remove(session);
    }

    在此程式碼中,我們從連線池中刪除連線,並打印出連線已關閉的訊息。

    配置WebSocket支持

    最後,需要配置Spring Boot以支持WebSocket。建立一個新的Java類,並添加註釋 @Configuration @EnableWebSocket 。然後,需要覆蓋方法 registerWebSocketHandlers() ,並指定WebSocket處理常式。下面是一個範例程式碼:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    @Configuration
    @EnableWebSocket
    public classWebSocketConfigimplementsWebSocketConfigurer{
    @Override
    publicvoidregisterWebSocketHandlers(WebSocketHandlerRegistry registry){
    registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");
    }
    }

    在此程式碼中,我們建立了一個新的 WebSocketServer 物件,並將其添加到WebSocket處理常式中。我們還指定了WebSocket端點( /websocket )和允許的來源( * )。

    四、 WebSocket的訊息格式

    4.1 文本訊息和二進制訊息

    文本訊息是普通的Unicode文本字串。當WebSocket連線建立時,客戶端和伺服器可以透過發送文本訊息來互相交換資訊。伺服器可以使用Session物件的 getBasicRemote() 方法來向客戶端發送文本訊息,客戶端可以使用WebSocket的 send() 方法來向伺服器發送文本訊息。

    下面是向客戶端發送文本訊息的範例程式碼:

    session.getBasicRemote().sendText("Hello, client!");

    二進制訊息可以是任意型別的數據,包括影像、音訊、視訊等。要向客戶端發送二進制訊息,伺服器可以使用Session物件的 getBasicRemote() 方法,將訊息作為ByteBuffer物件發送。客戶端可以使用WebSocket的send()方法來向伺服器發送二進制訊息。

    下面是向客戶端發送二進制訊息的範例程式碼:

    byte[] data = // binary data
    ByteBuffer buffer = ByteBuffer.wrap(data);
    session.getBasicRemote().sendBinary(buffer);

    請註意,盡管文本訊息和二進制訊息在格式上有所不同,但它們都是透過WebSocket發送的訊息型別,因此客戶端和伺服器都需要能夠處理這兩種型別的訊息。

    4.2 Ping和Pong訊息

    WebSocket還支持Ping和Pong訊息型別,用於檢測WebSocket連線是否仍然處於活動狀態。Ping訊息由客戶端發送到伺服器,Pong訊息由伺服器發送回客戶端作為響應。如果客戶端在一段時間內沒有收到Pong訊息,則它可以假定WebSocket連線已斷開,並關閉連線。

    要發送Ping訊息,請使用Session物件的 getBasicRemote() 方法,並將Ping訊息作為ByteBuffer物件發送。客戶端可以使用WebSocket的 sendPing() 方法來向伺服器發送Ping訊息。

    下面是向客戶端發送Ping訊息的範例程式碼:

    ByteBuffer pingMessage = ByteBuffer.wrap(newbyte[] { 8910 });
    session.getBasicRemote().sendPing(pingMessage);

    要接收Pong訊息,請在您的WebSocket處理常式中實作 onPong() 方法。當您的WebSocket伺服器接收到Pong訊息時,它將自動呼叫此方法,並將接收到的Pong訊息作為ByteBuffer物件傳遞給它。

    下面是實作onPong()方法的範例程式碼:

    @OnMessage
    publicvoidonPong(Session session, ByteBuffer pongMessage){
    System.out.println("Received Pong message: " + pongMessage);
    }

    請註意,Ping和Pong訊息通常用於WebSocket連線的健康檢查。如果您希望在WebSocket連線中使用此功能,則應定期發送Ping訊息並等待Pong訊息的響應。

    4.3 關閉訊息

    WebSocket還支持關閉訊息型別,用於關閉WebSocket連線。關閉訊息可以由客戶端或伺服器發起,並且可以攜帶一個可選的狀態碼和關閉原因。當WebSocket連線關閉時,客戶端和伺服器都應該發送一個關閉訊息以結束連線。

    要發送關閉訊息,請使用Session物件的 getBasicRemote() 方法,並呼叫它的 sendClose() 方法。關閉訊息可以攜帶一個可選的狀態碼和關閉原因。如果您不希望發送狀態碼或關閉原因,則可以將它們設定為0和null。

    下面是向客戶端發送關閉訊息的範例程式碼:

    session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Closing from client."));

    要處理接收到的關閉訊息,請在您的WebSocket處理常式中實作 onClose() 方法。當您的WebSocket伺服器接收到關閉訊息時,它將自動呼叫此方法,並將接收到的狀態碼和關閉原因傳遞給它。

    下面是實作 onClose() 方法的範例程式碼:

    @OnClose
    publicvoidonClose(Session session, CloseReason closeReason){
    System.out.println("Connection closed: " + closeReason.getCloseCode() + " - " + closeReason.getReasonPhrase());
    }

    請註意,客戶端和伺服器都應該發送關閉訊息以結束WebSocket連線。如果只有一方發送了關閉訊息,則另一方可能無法正確地關閉連線,並且可能需要等待超時才能釋放資源。建議客戶端和伺服器在關閉連線時都發送關閉訊息,以確保連線正確地關閉。

    五、 WebSocket的效能

    5.1 與傳統的HTTP請求/響應模型比較

  • 雙向通訊效能更好: WebSocket協定使用單一的TCP連線,允許客戶端和伺服器在同一個連線上進行雙向通訊。這種即時的雙向通訊可以更快地傳輸數據,而不需要建立多個HTTP請求/響應連線。

  • 更小的網路流量: 與HTTP相比,WebSocket協定需要更少的網路流量來維護連線,因為它不需要在每個請求/響應交換中發送頭部資訊。

  • 更低的延遲: WebSocket協定允許伺服器主動向客戶端推播訊息,而不需要客戶端先發送請求。這種即時通訊可以減少響應延遲,並提高應用程式的效能。

  • 更好的伺服器資源管理: 由於WebSocket連線可以保持活動狀態,伺服器可以更好地管理客戶端連線,減少伺服器開銷和處理時間。

  • WebSocket協定的效能比傳統的HTTP請求/響應模型更好,特別是在即時通訊和低延遲方面。WebSocket協定適用於需要即時通訊和即時數據更新的應用程式,如線上聊天、多人遊戲、即時監控等。

    5.2 最佳化WebSocket的效能

  • 減少訊息大小: WebSocket 傳輸的數據大小對效能有很大影響。盡量減少訊息的大小,可以降低網路頻寬和伺服器負載。例如,可以使用二進制傳輸協定來代替文本傳輸,或使用壓縮演算法對訊息進行壓縮。

  • 使用CDN加速: 使用 CDN(內容分發網路)可以將靜態資源緩存到離使用者更近的節點上,提高傳輸速度和效能。CDN 可以緩存 Websocket 的初始握手請求,避免不必要的網路延遲。

  • 使用負載均衡: WebSocket 服務可以使用負載均衡來分配並平衡多個伺服器的負載。負載均衡可以避免單個伺服器被過載,並提高整個服務的可伸縮性。

  • 最佳化伺服端程式碼: WebSocket 伺服端程式碼的效能也是關鍵因素。使用高效的框架和演算法,避免使用過多的記憶體和 CPU 資源,可以提高伺服端的效能和響應速度。

  • 避免網路阻塞: WebSocket 的效能也會受到網路阻塞的影響。當有太多的連線同時請求數據時,伺服器的效能會下降。使用合適的執行緒池和異步 IO 操作可以避免網路阻塞,提高 WebSocket 服務的並行效能。

  • 六、 WebSocket的擴充套件套用和未來發展方向

  • 更加完善的標準規範: WebSocket 標準規範還有很多可以最佳化的地方,未來可能會繼續完善 WebSocket 的標準規範,以適應更加復雜的套用場景。

  • 更加安全的通訊方式: 由於 WebSocket 的開放性,使得它可能會受到一些安全威脅,未來可能會透過加密、身份驗證等方式來增強 WebSocket 的安全性。

  • 更好的相容性: WebSocket 協定需要在 HTTP 協定的基礎上建立連線,因此可能會遇到相容性問題,未來可能會透過技術手段來解決這些問題。

  • 更好的效能和可伸縮性: WebSocket 協定的效能和可伸縮性對於復雜的套用場景非常關鍵,未來可能會透過技術手段來進一步提高 WebSocket 的效能和可伸縮性。

  • 來源 :blog.csdn.net/bairo007/article/details/131779053


    END


    看完本文有收獲?請轉發分享給更多人

    關註「Java編程鴨」,提升Java技能

    關註Java編程鴨微信公眾號,後台回復:碼農大禮包可以獲取最新整理的技術資料一份。涵蓋Java 框架學習、架構師學習等!

    文章有幫助的話,在看,轉發吧。

    謝謝支持喲 (*^__^*)