最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成WebSocket實現(xiàn)后臺向前端推送信息

 更新時間:2024年02月02日 09:54:57   作者:242030  
在一次項目開發(fā)中,使用到了Netty網(wǎng)絡(luò)應(yīng)用框架,以及MQTT進行消息數(shù)據(jù)的收發(fā),這其中需要后臺來將獲取到的消息主動推送給前端,所以本文記錄了SpringBoot集成WebSocket實現(xiàn)后臺向前端推送信息的操作,需要的朋友可以參考下

SpringBoot 集成 WebSocket,實現(xiàn)后臺向前端推送信息

在一次項目開發(fā)中,使用到了Netty網(wǎng)絡(luò)應(yīng)用框架,以及MQTT進行消息數(shù)據(jù)的收發(fā),這其中需要后臺來將獲取到的消息主動推送給前端,于是就使用到了MQTT,特此記錄一下。

1、什么是websocket?

WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實現(xiàn)了客戶端與服務(wù)器全雙工通信,學過計算機網(wǎng)絡(luò)都知道,既然是全雙工,就說明了服務(wù)器可以主動發(fā)送信息給客戶端 。這與我們的推送技術(shù)或者是多人在線聊天的功能不謀而合。

在這里插入圖片描述

為什么不使用HTTP 協(xié)議呢?這是因為HTTP是單工通信,通信只能由客戶端發(fā)起,客戶端請求一下,服務(wù)器處理一下,這就太麻煩了。于是websocket應(yīng)運而生。

在這里插入圖片描述

下面我們就直接開始使用Springboot開始整合。以下案例都在我自己的電腦上測試成功,你可以根據(jù)自己的功能進行修改即可。

2、使用步驟

2.1 添加依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>springboot-websocket</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-websocket</name>
    <description>springboot-websocket</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.2 啟用Springboot對WebSocket的支持

package com.example.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @Description: 開啟WebSocket支持
 */
@Configuration
public class WebSocketConfig {

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

2.3 核心配置WebSocketServer

因為WebSocket是類似客戶端服務(wù)端的形式(采用ws協(xié)議),那么這里的WebSocketServer其實就相當于一個ws協(xié)議的Controller。

  • @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務(wù)器端,注解的值將被用于監(jiān)聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務(wù)器端。
  • 新建一個ConcurrentHashMap webSocketMap 用于接收當前userId的WebSocket,方便傳遞之間對userId進行推送消息。

下面是具體業(yè)務(wù)代碼:

package com.example.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Description:
 * @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務(wù)器端,
 * 注解的值將被用于監(jiān)聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務(wù)器端
 */

@Component
@Slf4j
@Service
@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {

    // 靜態(tài)變量,用來記錄當前在線連接數(shù)。應(yīng)該把它設(shè)計成線程安全的。
    private static int onlineCount = 0;
    //concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的MyWebSocket對象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
    //與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
    private Session session;
    //接收sid
    private String sid = "";

    /**
     * 連接建立成功調(diào)用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        //加入set中
        webSocketSet.add(this);
        this.sid = sid;
        //在線數(shù)加1
        addOnlineCount();
        try {
            sendMessage("conn_success");
            log.info("有新窗口開始監(jiān)聽:" + sid + ",當前在線人數(shù)為:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     */
    @OnClose
    public void onClose() {
        //從set中刪除
        webSocketSet.remove(this);
        //在線數(shù)減1
        subOnlineCount();
        //斷開連接情況下,更新主板占用情況為釋放
        log.info("釋放的sid為:" + sid);
        //這里寫你 釋放的時候,要處理的業(yè)務(wù)
        log.info("有一連接關(guān)閉!當前在線人數(shù)為" + getOnlineCount());
    }

    /**
     * 收到客戶端消息后調(diào)用的方法
     *
     * @Param message 客戶端發(fā)送過來的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到來自窗口" + sid + "的信息:" + message);
        //群發(fā)消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Param session
     * @Param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發(fā)生錯誤");
        error.printStackTrace();
    }

    /**
     * 實現(xiàn)服務(wù)器主動推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群發(fā)自定義消息
     */
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        log.info("推送消息到窗口" + sid + ",推送內(nèi)容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                //這里可以設(shè)定只推送給這個sid的,為null則全部推送
                if (sid == null) {
                    // item.sendMessage(message);
                } else if (item.sid.equals(sid)) {
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

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

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

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

    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
        return webSocketSet;
    }
}

配置文件

spring.web.resources.static-locations=classpath:/static

2.4 測試Controller

package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping("/index")
    public String index() {
        return "index.html";
    }
}

2.5 啟動類

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

2.6 測試頁面index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Java后端WebSocket的Tomcat實現(xiàn)</title>
    <script type="text/javascript" src="js/jquery.min.js"></script>
</head>

<body>
<h1>Welcome</h1>

<input id="text" type="text" />
<button onclick="send()">發(fā)送消息</button>
<hr/>
<button onclick="closeWebSocket()">關(guān)閉WebSocket連接</button>
<hr/>
<div id="message"></div>
</body>
<script type="text/javascript">
    var websocket = null;
    //判斷當前瀏覽器是否支持WebSocket
    if('WebSocket' in window) {
        //改成你的地址
        websocket = new WebSocket("ws://127.0.0.1:8080/api/websocket/100");
    } else {
        alert('當前瀏覽器 Not support websocket')
    }

    //連接發(fā)生錯誤的回調(diào)方法
    websocket.onerror = function() {
        setMessageInnerHTML("WebSocket連接發(fā)生錯誤");
    };

    //連接成功建立的回調(diào)方法
    websocket.onopen = function() {
        setMessageInnerHTML("WebSocket連接成功");
    }
    var U01data, Uidata, Usdata
    //接收到消息的回調(diào)方法
    websocket.onmessage = function(event) {
        console.log(event);
        setMessageInnerHTML(event);
        setechart()
    }

    //連接關(guān)閉的回調(diào)方法
    websocket.onclose = function() {
        setMessageInnerHTML("WebSocket連接關(guān)閉");
    }

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

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

    //關(guān)閉WebSocket連接
    function closeWebSocket() {
        websocket.close();
    }

    //發(fā)送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send('{"msg":"' + message + '"}');
        setMessageInnerHTML(message + "&#13;");
    }
</script>

</html>

2.7 結(jié)果展示

啟動瀏覽器窗口訪問:http://localhost:8080/index

后臺出現(xiàn):

2022-08-16 18:18:11.931 INFO 796 --- [nio-8080-exec-3] com.example.websocket.WebSocketServer : 有新窗口開始監(jiān)聽:100,當前在線人數(shù)為:1

多次訪問:

2022-08-16 18:18:11.931  INFO 796 --- [nio-8080-exec-3] com.example.websocket.WebSocketServer    : 有新窗口開始監(jiān)聽:100,當前在線人數(shù)為:1
2022-08-16 18:19:27.054  INFO 796 --- [nio-8080-exec-6] com.example.websocket.WebSocketServer    : 釋放的sid為:100
2022-08-16 18:19:27.055  INFO 796 --- [nio-8080-exec-6] com.example.websocket.WebSocketServer    : 有一連接關(guān)閉!當前在線人數(shù)為0
2022-08-16 18:19:27.107  INFO 796 --- [nio-8080-exec-9] com.example.websocket.WebSocketServer    : 有新窗口開始監(jiān)聽:100,當前在線人數(shù)為:1
2022-08-16 18:19:28.332  INFO 796 --- [io-8080-exec-10] com.example.websocket.WebSocketServer    : 釋放的sid為:100
2022-08-16 18:19:28.332  INFO 796 --- [io-8080-exec-10] com.example.websocket.WebSocketServer    : 有一連接關(guān)閉!當前在線人數(shù)為0
2022-08-16 18:19:28.383  INFO 796 --- [nio-8080-exec-3] com.example.websocket.WebSocketServer    : 有新窗口開始監(jiān)聽:100,當前在線人數(shù)為:1
2022-08-16 18:19:29.811  INFO 796 --- [nio-8080-exec-4] com.example.websocket.WebSocketServer    : 釋放的sid為:100
2022-08-16 18:19:29.811  INFO 796 --- [nio-8080-exec-4] com.example.websocket.WebSocketServer    : 有一連接關(guān)閉!當前在線人數(shù)為0
2022-08-16 18:19:29.860  INFO 796 --- [nio-8080-exec-7] com.example.websocket.WebSocketServer    : 有新窗口開始監(jiān)聽:100,當前在線人數(shù)為:1

前臺顯示:

在這里插入圖片描述

發(fā)送消息:

在這里插入圖片描述

后臺顯示:

2022-08-16 18:21:58.602  INFO 796 --- [nio-8080-exec-4] com.example.websocket.WebSocketServer    : 收到來自窗口100的信息:{"msg":"hello world"}
2022-08-16 18:22:03.098  INFO 796 --- [nio-8080-exec-5] com.example.websocket.WebSocketServer    : 收到來自窗口100的信息:{"msg":"hihao"}

3、總結(jié)

這中間我遇到一個問題,就是說WebSocket啟動的時候優(yōu)先于spring容器,從而導致在WebSocketServer中調(diào)用業(yè)務(wù)Service會報空指針異常,所以需要在WebSocketServer中將所需要用到的service給靜態(tài)初始化一下:如下所示:

@Component
@Slf4j
@Service
@ServerEndpoint("/api/websocket/{sid}")
public class WebSocketServer {
    // 靜態(tài)初始化用到的Service
    public static IDetRecordService detRecordService;
}

還需要做如下配置:

@Configuration
public class WebSocketConfig {

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

    @Autowired
    // 提前注入Spring
    private void setRedisService(IDetRecordService iDetRecordService){
        WebSocketServer.detRecordService= iDetRecordService;
    }
}

以上就是SpringBoot集成WebSocket實現(xiàn)后臺向前端推送信息的詳細內(nèi)容,更多關(guān)于SpringBoot WebSocket推送消息的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot整合HTTPS的項目實踐

    SpringBoot整合HTTPS的項目實踐

    HTTPS的主要作用是通過SSL證書保護用戶數(shù)據(jù)的安全與隱私,增加網(wǎng)站信任度,防止數(shù)據(jù)被竊取和篡改,保護網(wǎng)站免受釣魚攻擊,本文就來介紹一下,感興趣的可以了解一下
    2024-10-10
  • SpringCloud之Zuul服務(wù)網(wǎng)關(guān)詳解

    SpringCloud之Zuul服務(wù)網(wǎng)關(guān)詳解

    這篇文章主要介紹了SpringCloud之Zuul服務(wù)網(wǎng)關(guān)詳解,服務(wù)網(wǎng)關(guān)是微服務(wù)架構(gòu)中一個不可或缺的部分,通過服務(wù)網(wǎng)關(guān)統(tǒng)一向外系統(tǒng)提供REST?API的過程中,除了具備服務(wù)路由、均衡負載功能之外,它還具備了權(quán)限控制(鑒權(quán))等功能,需要的朋友可以參考下
    2023-08-08
  • Java阻塞隊列BlockingQueue詳解

    Java阻塞隊列BlockingQueue詳解

    這篇文章主要介紹了Java阻塞隊列BlockingQueue,文章通過隊列的類型展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-07-07
  • idea切換分支的時候,忽略一些無用的修改設(shè)置

    idea切換分支的時候,忽略一些無用的修改設(shè)置

    這篇文章主要介紹了idea切換分支的時候,忽略一些無用的修改操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 新版idea創(chuàng)建spring boot項目的詳細教程

    新版idea創(chuàng)建spring boot項目的詳細教程

    這篇文章給大家介紹了新版idea創(chuàng)建spring boot項目的詳細教程,本教程對新手小白友好,若根據(jù)教程創(chuàng)建出現(xiàn)問題導致失敗可下載我提供的源碼,在文章最后,本教程較新,文中通過圖文給大家介紹的非常詳細,感興趣的朋友可以參考下
    2024-01-01
  • Springboot Caffeine本地緩存使用示例

    Springboot Caffeine本地緩存使用示例

    這篇文章主要介紹了Springboot Caffeine本地緩存使用示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • JVM代碼運行邏輯解讀

    JVM代碼運行邏輯解讀

    這篇文章主要介紹了JVM代碼運行邏輯,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Java實現(xiàn)簡單聊天機器人

    Java實現(xiàn)簡單聊天機器人

    這篇文章主要為大家詳細介紹了Java實現(xiàn)簡單聊天機器人,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Java獲取指定父節(jié)點、子節(jié)點的方法實現(xiàn)

    Java獲取指定父節(jié)點、子節(jié)點的方法實現(xiàn)

    在Java中,要獲取指定節(jié)點的父節(jié)點和子節(jié)點,通常需要使用 DOM,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
    2024-02-02
  • java反編譯工具jd-gui使用詳解

    java反編譯工具jd-gui使用詳解

    JD-GUI是一個獨立的圖形實用程序,顯示“.class”文件的Java源代碼,本文主要介紹了java反編譯工具jd-gui使用詳解,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09

最新評論

潮州市| 邻水| 垦利县| 威远县| 罗定市| 东兰县| 大荔县| 南召县| 林芝县| 大关县| 慈溪市| 固原市| 莱芜市| 民权县| 微博| 错那县| 南漳县| 合水县| 东辽县| 简阳市| 莫力| 滨州市| 边坝县| 大名县| 正蓝旗| 潮安县| 滁州市| 桃源县| 渝北区| 凭祥市| 崇明县| 兴城市| 大姚县| 丽江市| 塔河县| 齐齐哈尔市| 乌兰察布市| 克东县| 通辽市| 武清区| 巴中市|