亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

1、什么是websocket?

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

在這里插入圖片描述

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

在這里插入圖片描述

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

2、使用步驟

2.1 添加依賴(lài)

<?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對(duì)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: 開(kāi)啟WebSocket支持
 */
@Configuration
public class WebSocketConfig {

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

2.3 核心配置WebSocketServer

因?yàn)閃ebSocket是類(lèi)似客戶(hù)端服務(wù)端的形式(采用ws協(xié)議),那么這里的WebSocketServer其實(shí)就相當(dāng)于一個(gè)ws協(xié)議的Controller。

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

下面是具體業(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 注解是一個(gè)類(lèi)層次的注解,它的功能主要是將目前的類(lèi)定義成一個(gè)websocket服務(wù)器端,
 * 注解的值將被用于監(jiān)聽(tīng)用戶(hù)連接的終端訪(fǎng)問(wèn)URL地址,客戶(hù)端可以通過(guò)這個(gè)URL來(lái)連接到WebSocket服務(wù)器端
 */

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

    // 靜態(tài)變量,用來(lái)記錄當(dāng)前在線(xiàn)連接數(shù)。應(yīng)該把它設(shè)計(jì)成線(xiàn)程安全的。
    private static int onlineCount = 0;
    //concurrent包的線(xiàn)程安全Set,用來(lái)存放每個(gè)客戶(hù)端對(duì)應(yīng)的MyWebSocket對(duì)象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
    //與某個(gè)客戶(hù)端的連接會(huì)話(huà),需要通過(guò)它來(lái)給客戶(hù)端發(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;
        //在線(xiàn)數(shù)加1
        addOnlineCount();
        try {
            sendMessage("conn_success");
            log.info("有新窗口開(kāi)始監(jiān)聽(tīng):" + sid + ",當(dāng)前在線(xiàn)人數(shù)為:" + getOnlineCount());
        } catch (IOException e) {
            log.error("websocket IO Exception");
        }
    }

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

    /**
     * 收到客戶(hù)端消息后調(diào)用的方法
     *
     * @Param message 客戶(hù)端發(fā)送過(guò)來(lái)的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到來(lái)自窗口" + 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ā)生錯(cuò)誤");
        error.printStackTrace();
    }

    /**
     * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送
     */
    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è)定只推送給這個(gè)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 測(cè)試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 啟動(dòng)類(lèi)

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 測(cè)試頁(yè)面index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Java后端WebSocket的Tomcat實(shí)現(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;
    //判斷當(dāng)前瀏覽器是否支持WebSocket
    if('WebSocket' in window) {
        //改成你的地址
        websocket = new WebSocket("ws://127.0.0.1:8080/api/websocket/100");
    } else {
        alert('當(dāng)前瀏覽器 Not support websocket')
    }

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

    //連接成功建立的回調(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)聽(tīng)窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí),主動(dòng)去關(guān)閉websocket連接,防止連接還沒(méi)斷開(kāi)就關(guān)閉窗口,server端會(huì)拋異常。
    window.onbeforeunload = function() {
        closeWebSocket();
    }

    //將消息顯示在網(wǎng)頁(yè)上
    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é)果展示

啟動(dòng)瀏覽器窗口訪(fǎng)問(wèn):http://localhost:8080/index

后臺(tái)出現(xiàn):

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

多次訪(fǎng)問(wèn):

2022-08-16 18:18:11.931  INFO 796 --- [nio-8080-exec-3] com.example.websocket.WebSocketServer    : 有新窗口開(kāi)始監(jiān)聽(tīng):100,當(dāng)前在線(xiàn)人數(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)閉!當(dāng)前在線(xiàn)人數(shù)為0
2022-08-16 18:19:27.107  INFO 796 --- [nio-8080-exec-9] com.example.websocket.WebSocketServer    : 有新窗口開(kāi)始監(jiān)聽(tīng):100,當(dāng)前在線(xiàn)人數(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)閉!當(dāng)前在線(xiàn)人數(shù)為0
2022-08-16 18:19:28.383  INFO 796 --- [nio-8080-exec-3] com.example.websocket.WebSocketServer    : 有新窗口開(kāi)始監(jiān)聽(tīng):100,當(dāng)前在線(xiàn)人數(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)閉!當(dāng)前在線(xiàn)人數(shù)為0
2022-08-16 18:19:29.860  INFO 796 --- [nio-8080-exec-7] com.example.websocket.WebSocketServer    : 有新窗口開(kāi)始監(jiān)聽(tīng):100,當(dāng)前在線(xiàn)人數(shù)為:1

前臺(tái)顯示:

在這里插入圖片描述

發(fā)送消息:

在這里插入圖片描述

后臺(tái)顯示:

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

3、總結(jié)

這中間我遇到一個(gè)問(wèn)題,就是說(shuō)WebSocket啟動(dòng)的時(shí)候優(yōu)先于spring容器,從而導(dǎo)致在WebSocketServer中調(diào)用業(yè)務(wù)Service會(huì)報(bào)空指針異常,所以需要在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實(shí)現(xiàn)后臺(tái)向前端推送信息的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot WebSocket推送消息的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中volatile關(guān)鍵字的作用

    Java中volatile關(guān)鍵字的作用

    這篇文章主要介紹了Java中volatile關(guān)鍵字的作用,文章基于Java的相關(guān)資料展開(kāi)對(duì)volatile關(guān)鍵字作用的詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-04-04
  • MyBatis在SQL語(yǔ)句中如何獲取list的大小

    MyBatis在SQL語(yǔ)句中如何獲取list的大小

    這篇文章主要介紹了MyBatis在SQL語(yǔ)句中如何獲取list的大小問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java GUI圖形界面開(kāi)發(fā)實(shí)現(xiàn)小型計(jì)算器流程詳解

    Java GUI圖形界面開(kāi)發(fā)實(shí)現(xiàn)小型計(jì)算器流程詳解

    本文章向大家介紹Java GUI圖形界面開(kāi)發(fā)實(shí)現(xiàn)小型計(jì)算器,主要包括布局管理器使用實(shí)例、應(yīng)用技巧、基本知識(shí)點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-08-08
  • Springboot整合Spring Cloud Kubernetes讀取ConfigMap支持自動(dòng)刷新配置的教程

    Springboot整合Spring Cloud Kubernetes讀取ConfigMap支持自動(dòng)刷新配置的教程

    這篇文章主要介紹了Springboot整合Spring Cloud Kubernetes讀取ConfigMap支持自動(dòng)刷新配置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 如何解決Spring事務(wù)注解@Transactional在類(lèi)內(nèi)部方法調(diào)用不生效

    如何解決Spring事務(wù)注解@Transactional在類(lèi)內(nèi)部方法調(diào)用不生效

    這篇文章主要介紹了如何解決Spring事務(wù)注解@Transactional在類(lèi)內(nèi)部方法調(diào)用不生效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 如何使用jmeter錄制瀏覽器Https請(qǐng)求過(guò)程圖解

    如何使用jmeter錄制瀏覽器Https請(qǐng)求過(guò)程圖解

    這篇文章主要介紹了基于jmeter錄制瀏覽器Https請(qǐng)求過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java純代碼實(shí)現(xiàn)導(dǎo)出pdf合并單元格

    Java純代碼實(shí)現(xiàn)導(dǎo)出pdf合并單元格

    這篇文章主要為大家詳細(xì)介紹了Java如何純代碼實(shí)現(xiàn)導(dǎo)出pdf與合并單元格功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • JavaGUI常用三種布局使用介紹

    JavaGUI常用三種布局使用介紹

    這篇文章主要介紹了JavaGUI常用三種布局-FlowLayout、BorderLayout、GridLayout,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-03-03
  • SpringBoot如何解析參數(shù)的深入理解

    SpringBoot如何解析參數(shù)的深入理解

    這篇文章主要給大家介紹了關(guān)于SpringBoot是如何解析參數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Java 基礎(chǔ)之事務(wù)詳細(xì)介紹

    Java 基礎(chǔ)之事務(wù)詳細(xì)介紹

    這篇文章主要介紹了Java 基礎(chǔ)之事務(wù)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2017-03-03

最新評(píng)論