springboot - websocket實現及原理


本文章包括websocket面試相關問題以及spring boot如何整合webSocket。

參考文檔 https://blog.csdn.net/prayallforyou/article/details/53737901 、https://www.cnblogs.com/bianzy/p/5822426.html

  webSocket是HTML5的一種新協議,它實現了服務端與客戶端的全雙工通信,建立在傳輸層,tcp協議之上,即瀏覽器與服務端需要先建立tcp協議,再發送webSocket連接建立請求。

  webSocket的連接:客戶端發送請求信息,服務端接受到請求並返回相應的信息。連接建立。客戶端發送http請求時,通過  Upgrade:webSocket Connection:Upgrade 告知服務器需要建立的是webSocket連接,並且還會傳遞webSocket版本號,協議的字版本號,原始地址,主機地址等等。

  webSocket相互通信的Header很小,大概只有2Bytes。


  以下是基於spring boot及支持webScoket的高版本瀏覽器的配置過程。

一、pom.xml中引入webSocket組件

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

二、后台引入webSocket

  要想讓一個類處理webScoket的請求,需要兩個東西:

  ①  類名上加webScoket請求攔截注釋@ServerEndpoint(value="/***")

    類需要繼承org.springframework.web.socket.server.standard.ServerEndpointExporter,重寫交互過程中各種情況下調用的方法(建立時、斷開時、出錯時、接收消息、發送消息)

  針對②,一方面根據spring的IOC特性,需要反向代理,另一方面因為webSocket是一個功能而不僅僅是屬於某個業務,所以應當在配置文件中聲明。配置文件可以是xml文件,也可以是注釋了@Configuration的類文件,根據個人喜好使用~,這里使用的是@Configuration。

@Configuration
public class ProjectConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

  使用@ServerEndpoint(value="/***") 時會自動注入返回類型為ServerEndpointExporter的bean。等同於繼承了ServerEndpointExporter類。繼承類后再重寫onOpen、onClose、onMessage、onError方法,因為不是直接使用繼承,所以方法的重寫也需要使用注釋,代碼如下

package com.example.SpringBootTry.controller.webSocket;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

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

import org.springframework.stereotype.Component;

/**
 * 雙工通信websocket工具類
 * @author wwl
 *
 */
@ServerEndpoint(value="/webSocket")
@Component
public class WebSocketUtil{
    //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。
    private static int onlineCount = 0;

    //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。
    private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<WebSocketUtil>();

    //與某個客戶端的連接會話,需要通過它來給客戶端發送數據
    private Session session;

    /**
     * 連接建立成功調用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在線數加1
        System.out.println("有新連接加入!當前在線人數為" + getOnlineCount());
        try {
            sendMessage("您是第" + getOnlineCount() + "個雙工通信的用戶!");
        } catch (IOException e) {
            System.out.println("IO異常");
        }
    }

    /**
     * 連接關閉調用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //從set中刪除
        subOnlineCount();           //在線數減1
        System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount());
    }

    /**
     * 收到客戶端消息后調用的方法
     *
     * @param message 客戶端發送過來的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("來自客戶端的消息:" + message);
        //發送消息
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 發生錯誤時調用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("發生錯誤");
        error.printStackTrace();
    }

    /**
     * 發送消息
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }


    /**
     * 群發自定義消息
     * */
    public static void sendInfo(String message) throws IOException {
        for (WebSocketUtil item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketUtil.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketUtil.onlineCount--;
    }
}

 三、前端引入webSocket

  使用  var websocket = new WebSocket("ws://localhost:8081/***")  建立webSocket連接,定義websocket的onerror、onopen、onmessage、onclose的屬性,跟后台的四個方法相對應,完成合理的webSocket交互。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>websocket測試頁面</title>    
    <meta http-equiv="keywords" content="websocket,例子">
    <meta http-equiv="description" content="測試websocket">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
    Welcome<br/>
    <input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
    <div id="message">
    </div>    
</body>
<script type="text/javascript">
    var websocket = null;

    //判斷當前瀏覽器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8081/webSocket");
    }else{
        alert('Not support websocket')
    }

    //連接發生錯誤的回調方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //連接成功建立的回調方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

    //接收到消息的回調方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //連接關閉的回調方法
    websocket.onclose = function(){ 
        setMessageInnerHTML("close");
    }

    //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //將消息顯示在網頁上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //關閉連接
    function closeWebSocket(){
        websocket.close();
    }

    //發送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

以上為spring boot 整合webSocket的一些入門知識。有錯誤歡迎指正。

demo地址:https://github.com/ttjsndx/someDemo/blob/master/SpringBootTryDemo.rar

 


免責聲明!

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



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