目錄
Spring Boot端
第一步,引入依賴
第二步,建立WebSocket配置類別
第三步,建立WebSocket服務
第四步,建立Controller進行發送測試
Vue端
第一步,建立連接工具類別
第二步,建立連線
第三步,監聽伺服器發送過來的訊息
首頁 Java java教程 怎麼使用Spring Boot+Vue實現Socket通知推播

怎麼使用Spring Boot+Vue實現Socket通知推播

May 27, 2023 am 08:47 AM
vue socket springboot

Spring Boot端

第一步,引入依賴

首先我們需要引入WebSocket所需的依賴,以及處理輸出格式的依賴

<!--格式转换-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.73</version>
</dependency>
<!--WebSocket依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
登入後複製

第二步,建立WebSocket配置類別

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
 
/**
 * @author: tjp
 * @create: 2023-04-03 09:58
 * @Description: WebSocket配置
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
 
}
登入後複製

第三步,建立WebSocket服務

這一步我們透過userId作為標識符,區分系統中對應的用戶,後續也可基於此,進行其他的操作步驟。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.excel.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * @author: tjp
 * @create: 2023-04-03 13:55
 * @Description: WebSocket服务
 */
 
@ServerEndpoint("/websocket/{userId}")
@Slf4j
@Component
public class WebSocketServer {
    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;
    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userId
     */
    private String userId = "";
 
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            //加入set中
        } else {
            webSocketMap.put(userId, this);
            //加入set中
            addOnlineCount();
            //在线数加1
        }
 
        log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());
 
        try {
            HashMap<Object, Object> map = new HashMap<>();
            map.put("key", "连接成功");
            sendMessage(JSON.toJSONString(map));
        } catch (IOException e) {
            log.error("用户:" + userId + ",网络异常!!!!!!");
        }
    }
 
 
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
    }
 
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:" + userId + ",报文:" + message);
        //可以群发消息
        //消息保存到数据库、redis
        if (StringUtils.isNotBlank(message)) {
            try {
                //解析发送的报文
                JSONObject jsonObject = JSONObject.parseObject(message);
                //追加发送人(防止串改)
                jsonObject.put("fromUserId", this.userId);
                String fromUserId = jsonObject.getString("fromUserId");
                //传送给对应toUserId用户的websocket
                if (StringUtils.isNotBlank(fromUserId) && webSocketMap.containsKey(fromUserId)) {
                    webSocketMap.get(fromUserId).sendMessage(jsonObject.toJSONString());
                    //自定义-业务处理
 
//                    DeviceLocalThread.paramData.put(jsonObject.getString("group"),jsonObject.toJSONString());
                } else {
                    log.error("请求的userId:" + fromUserId + "不在该服务器上");
                    //否则不在这个服务器上,发送到mysql或者redis
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * 发生错误时候
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }
 
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        //加入线程锁
        synchronized (session) {
            try {
                //同步发送信息
                this.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("服务器推送失败:" + e.getMessage());
            }
        }
    }
 
 
    /**
     * 发送自定义消息
     * */
    /**
     * 发送自定义消息
     *
     * @param message  发送的信息
     * @param toUserId 如果为null默认发送所有
     * @throws IOException
     */
    public static void sendInfo(String message, String toUserId) throws IOException {
        //如果userId为空,向所有群体发送
        if (StringUtils.isEmpty(toUserId)) {
            //向所有用户发送信息
            Iterator<String> itera = webSocketMap.keySet().iterator();
            while (itera.hasNext()) {
                String keys = itera.next();
                WebSocketServer item = webSocketMap.get(keys);
                item.sendMessage(message);
            }
        }
        //如果不为空,则发送指定用户信息
        else if (webSocketMap.containsKey(toUserId)) {
            WebSocketServer item = webSocketMap.get(toUserId);
            item.sendMessage(message);
        } else {
            log.error("请求的userId:" + toUserId + "不在该服务器上");
        }
    }
 
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
 
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
 
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
 
    public static synchronized ConcurrentHashMap<String, WebSocketServer> getWebSocketMap() {
        return WebSocketServer.webSocketMap;
    }
 
}
登入後複製

第四步,建立Controller進行發送測試

取得目前線上人數

import com.......WebSocketServer;
 
@ApiOperation(value = "获取当前在线人数")
@GetMapping("/getOnlineCount")
public Integer getOnlineCount() {
    return WebSocketServer.getOnlineCount();
}
登入後複製

透過接口,向前端使用者推送訊息

import com.......WebSocketServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.io.IOException;
 
/**
 * @author: tjp
 * @create: 2023-04-03 13:57
 * @Description: 测试
 */
@RestController
@RequestMapping("/news")
public class NewsController {
    @GetMapping("/send")
    public String send() {
        try {
            WebSocketServer.sendInfo("这是websocket发送过来的消息!", "需要推送的用户的编号");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return "发送消息成功";
    }
 
}
登入後複製

Vue端

第一步,建立連接工具類別

建立工具類別websocket.js,這裡的userId就是用來作為識別符的userId

/**
 * @author: tjp
 * @create: 2023-04-03 11:22
 * @Description: Socket客户端
 */
export class WebSocketClient {
  constructor(userId) {
    this.userId = userId;
    this.websocket = null;
    this.timeout = 10000; // 心跳超时时间,单位ms
    this.timeoutObj = null; // 心跳定时器
    this.serverTimeoutObj = null; // 服务器超时定时器
    this.lockReconnect = false; // 避免重复连接
    this.timeoutnum = null; // 重连延迟定时器
  }
 
  // 初始化WebSocket连接
  initWebSocket() {
    let wsUrl = `ws://127.0.0.1:8080/websocket/${this.userId}`;
    this.websocket = new WebSocket(wsUrl);
    this.websocket.onopen = this.websocketonopen.bind(this);
    this.websocket.onerror = this.websocketonerror.bind(this);
    this.websocket.onmessage = this.setOnmessageMessage.bind(this);
    this.websocket.onclose = this.websocketclose.bind(this);
    // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = this.websocketclose.bind(this);
  }
 
  // 启动心跳
  start() {
    console.log(&#39;start&#39;);
    // 清除延时器
    this.timeoutObj && clearTimeout(this.timeoutObj);
    this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
    /*// 向服务器发送心跳消息
    let actions = { "test": "12345" };
    this.websocket && this.websocket.readyState == 1 && this.websocket.send(JSON.stringify(actions));
    // 启动心跳定时器
    this.timeoutObj = setTimeout(() => {
      this.start();
      // 定义一个延时器等待服务器响应,若超时,则关闭连接,重新请求server建立socket连接
      this.serverTimeoutObj = setTimeout(() => {
        this.websocket.close();
      }, this.timeout)
    }, this.timeout)*/
  }
 
  // 重置心跳
  reset() {
    // 清除时间
    clearTimeout(this.timeoutObj);
    clearTimeout(this.serverTimeoutObj);
    // 重启心跳
    this.start();
  }
 
  // 重新连接
  reconnect() {
    if (this.lockReconnect) return;
    this.lockReconnect = true;
    // 没连接上会一直重连,设置延迟避免请求过多
    this.timeoutnum && clearTimeout(this.timeoutnum);
    this.timeoutnum = setTimeout(() => {
      this.initWebSocket();
      this.lockReconnect = false;
    }, 5000)
  }
 
  // 处理收到的消息
  async setOnmessageMessage(event) {
    console.log(event.data, &#39;获得消息&#39;);
    // 重置心跳
    // this.reset();
    // 自定义全局监听事件
    window.dispatchEvent(new CustomEvent(&#39;onmessageWS&#39;, {
      detail: {
        data: event.data
      }
    }))
    // //发现消息进入    开始处理前端触发逻辑
    // if (event.data === &#39;success&#39; || event.data === &#39;heartBath&#39;) return
  }
 
  // WebSocket连接成功回调
  websocketonopen() {
    // 开启心跳
    this.start();
    console.log("WebSocket连接成功!!!" + new Date() + "----" + this.websocket.readyState);
    clearInterval(this.otimer);//停止
  }
 
  // WebSocket连接错误回调
  websocketonerror(e) {
    console.log("WebSocket连接发生错误" + e);
  }
 
  // WebSocket连接关闭回调
  websocketclose(e) {
    this.websocket.close();
    clearTimeout(this.timeoutObj);
    clearTimeout(this.serverTimeoutObj);
    console.log("websocketcloe关闭连接")
  }
 
  // 关闭WebSocket连接
  closeWebSocket() {
    this.websocket.close();
    console.log("closeWebSocket关闭连接")
  }
 
  // 监听窗口关闭事件
  onbeforeunload() {
    this.closeWebSocket();
  }
}
登入後複製

第二步,建立連線

在任意你想建立連線的頁面中建立Socket連線

例如,在使用者點擊登入按鈕之後

在這裡可以使用原型,建立連線對象,並啟動連線

<script>
import Vue from "vue";
import {WebSocketClient} from "@/utils/websocket";
......
......
methods:{
 
 handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch(&#39;user/login&#39;, this.loginForm).then(() => {
            this.$router.push({path: this.redirect || &#39;/&#39;})
            this.loading = false
            /*-----------在此处放入原型中------------*/
            Vue.prototype.$WebSocketClientInstance = new WebSocketClient(&#39;t&#39;);
            Vue.prototype.$WebSocketClientInstance.initWebSocket()
            /*-----------------end------------*/
          }).catch(() => {
            this.loading = false
          })
        } else {
          this.$message({message: &#39;请填写正确格式的用户名或密码&#39;, type: &#39;error&#39;})
          return false
        }
      })
    }    
}
 
.....
.....
</script>
登入後複製

怎麼使用Spring Boot+Vue實現Socket通知推播

第三步,監聽伺服器發送過來的訊息

在你想監聽的頁面,使用監聽器進行監聽

<script>
....
....
mounted() {
    // 添加socket通知监听
    window.addEventListener(&#39;onmessageWS&#39;, this.getSocketData)
},
methods: {
    // 收到消息处理
    getSocketData(res) {
      console.log(res.detail)
      console.log("llll")
    },
}
 
....
....
</script>
登入後複製

這個時候,你就可以透過後端的介面進行發送了

 搞個測試

怎麼使用Spring Boot+Vue實現Socket通知推播

怎麼使用Spring Boot+Vue實現Socket通知推播

##第四步,關閉連線 

搞個按鈕

<template>
  <div>
    <button @click="closeConnect">关闭连接</button>
  </div>
</template>
 
<script>
import {WebSocketClient} from "@/utils/websocket";
import Vue from "vue";
 
export default {
  methods: {
    closeConnect() {
      console.dir(Vue.prototype)
      Vue.prototype.$WebSocketClientInstance.closeWebSocket();
    },
  }
}
</script>
登入後複製

怎麼使用Spring Boot+Vue實現Socket通知推播#

以上是怎麼使用Spring Boot+Vue實現Socket通知推播的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

vue.js怎麼引用js文件 vue.js怎麼引用js文件 Apr 07, 2025 pm 11:27 PM

在 Vue.js 中引用 JS 文件的方法有三種:直接使用 &lt;script&gt; 標籤指定路徑;利用 mounted() 生命週期鉤子動態導入;通過 Vuex 狀態管理庫進行導入。

vue怎麼給按鈕添加函數 vue怎麼給按鈕添加函數 Apr 08, 2025 am 08:51 AM

可以通過以下步驟為 Vue 按鈕添加函數:將 HTML 模板中的按鈕綁定到一個方法。在 Vue 實例中定義該方法並編寫函數邏輯。

vue中的watch怎麼用 vue中的watch怎麼用 Apr 07, 2025 pm 11:36 PM

Vue.js 中的 watch 選項允許開發者監聽特定數據的變化。當數據發生變化時,watch 會觸發一個回調函數,用於執行更新視圖或其他任務。其配置選項包括 immediate,用於指定是否立即執行回調,以及 deep,用於指定是否遞歸監聽對像或數組的更改。

vue中怎麼用bootstrap vue中怎麼用bootstrap Apr 07, 2025 pm 11:33 PM

在 Vue.js 中使用 Bootstrap 分為五個步驟:安裝 Bootstrap。在 main.js 中導入 Bootstrap。直接在模板中使用 Bootstrap 組件。可選:自定義樣式。可選:使用插件。

Vue 實現跑馬燈/文字滾動效果 Vue 實現跑馬燈/文字滾動效果 Apr 07, 2025 pm 10:51 PM

在 Vue 中實現跑馬燈/文字滾動效果,可以使用 CSS 動畫或第三方庫。本文介紹了使用 CSS 動畫的方法:創建滾動文本,用 &lt;div&gt; 包裹文本。定義 CSS 動畫,設置 overflow: hidden、width 和 animation。定義關鍵幀,設置動畫開始和結束時的 transform: translateX()。調整動畫屬性,如持續時間、滾動速度和方向。

vue返回上一頁的方法 vue返回上一頁的方法 Apr 07, 2025 pm 11:30 PM

Vue.js 返回上一頁有四種方法:$router.go(-1)$router.back()使用 &lt;router-link to=&quot;/&quot;&gt; 組件window.history.back(),方法選擇取決於場景。

怎樣查詢vue的版本 怎樣查詢vue的版本 Apr 07, 2025 pm 11:24 PM

可以通過以下方法查詢 Vue 版本:使用 Vue Devtools 在瀏覽器的控制台中查看“Vue”選項卡。使用 npm 運行“npm list -g vue”命令。在 package.json 文件的“dependencies”對像中查找 Vue 項。對於 Vue CLI 項目,運行“vue --version”命令。檢查 HTML 文件中引用 Vue 文件的 &lt;script&gt; 標籤中的版本信息。

vue遍歷怎麼用 vue遍歷怎麼用 Apr 07, 2025 pm 11:48 PM

Vue.js 遍歷數組和對像有三種常見方法:v-for 指令用於遍歷每個元素並渲染模板;v-bind 指令可與 v-for 一起使用,為每個元素動態設置屬性值;.map 方法可將數組元素轉換為新數組。

See all articles