背景
? 項(xiàng)目中需要做一個(gè)消息提示功能,當(dāng)有用戶(hù)處理相關(guān)待辦信息后,別的用戶(hù)需要實(shí)時(shí)更新處理后的待辦信息。
解決方案:
? 1、使用最原始的方法,寫(xiě)個(gè)定時(shí)器去查詢(xún)待辦信息。但這種方式在大多數(shù)情況是不被允許的,它會(huì)浪費(fèi)系統(tǒng)中的許多資源,同時(shí)也并不是完全意義上的實(shí)時(shí)更新。
? 2、使用WebSocket通信技術(shù)去實(shí)現(xiàn)一個(gè)實(shí)時(shí)更新,它可以實(shí)現(xiàn)廣播和私信的模式。當(dāng)一個(gè)用戶(hù)與WebSocket服務(wù)建立連接后,用戶(hù)可以給它發(fā)送一個(gè)消息,此時(shí)WebSocket服務(wù)會(huì)接收到這個(gè)消息并做出回信(此時(shí)可以回信給所有與其建立連接的用戶(hù)——廣播,也可以回信給指定用戶(hù)——私信)。接下來(lái)將從前后端去講解WebSocket的使用。
一、WebSocket服務(wù)的搭建(SpringBoot后端)
SpringBoot自帶的WebSocket有以下5個(gè)注解需要注意:
@ServerEndpoint
暴露出的ws應(yīng)用的路徑,支持RESTful風(fēng)格傳參,類(lèi)似/websocket/{username}
@OnOpen
與當(dāng)前客戶(hù)端連接成功,有入?yún)ession對(duì)象(當(dāng)前連接對(duì)象),同時(shí)可以利用@PathParam()獲取上述應(yīng)用路徑中傳遞的參數(shù),比如@PathParam(“username”) String username。
@OnClose
與當(dāng)前客戶(hù)端連接失敗,有入?yún)ession對(duì)象(當(dāng)前連接對(duì)象),同時(shí)也可以利用@PathParam()獲取上述應(yīng)用路徑中傳遞的參數(shù)。
@OnError
與當(dāng)前客戶(hù)端連接異常,有入?yún)ession對(duì)象(當(dāng)前連接對(duì)象)、Throwable對(duì)象(異常對(duì)象),同時(shí)也可以利用@PathParam()獲取上述應(yīng)用路徑中傳遞的參數(shù)。
@OnMessage
當(dāng)前客戶(hù)端發(fā)送消息,有入?yún)ession對(duì)象(當(dāng)前連接對(duì)象)、String message對(duì)象(當(dāng)前客戶(hù)端傳遞過(guò)來(lái)的字符串消息)
1、引入所需依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、使用自定義類(lèi)開(kāi)啟WebSocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @program: webSocketTest
* @description: WebSocket相關(guān)配置
* @author: 黃珺瑜
* @create: 2022-06-30 16:24
**/
@Configuration
@EnableWebSocket
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
3、配置WebSocket服務(wù)
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @program: webSocketTest
* @description: WebSocket服務(wù)
* @author: 黃珺瑜
* @create: 2022-06-30 16:25
**/
@Component
@Slf4j
@ServerEndpoint("/websocket") //暴露的ws應(yīng)用的路徑
public class WebSocket {
// 用來(lái)存儲(chǔ)服務(wù)連接對(duì)象
private static Map<String ,Session> clientMap = new ConcurrentHashMap<>();
/**
* 客戶(hù)端與服務(wù)端連接成功
* @param session
*/
@OnOpen
public void onOpen(Session session){
/*
do something for onOpen
與當(dāng)前客戶(hù)端連接成功時(shí)
*/
clientMap.put(session.getId(),session);
}
/**
* 客戶(hù)端與服務(wù)端連接關(guān)閉
* @param session
*/
@OnClose
public void onClose(Session session){
/*
do something for onClose
與當(dāng)前客戶(hù)端連接關(guān)閉時(shí)
*/
clientMap.remove(session.getId());
}
/**
* 客戶(hù)端與服務(wù)端連接異常
* @param error
* @param session
*/
@OnError
public void onError(Throwable error,Session session) {
error.printStackTrace();
}
/**
* 客戶(hù)端向服務(wù)端發(fā)送消息
* @param message
* @throws IOException
*/
@OnMessage
public void onMsg(Session session,String message) throws IOException {
/*
do something for onMessage
收到來(lái)自當(dāng)前客戶(hù)端的消息時(shí)
*/
sendAllMessage(message);
}
//向所有客戶(hù)端發(fā)送消息(廣播)
private void sendAllMessage(String message){
Set<String> sessionIdSet = clientMap.keySet(); //獲得Map的Key的集合
// 此處相當(dāng)于一個(gè)廣播操作
for (String sessionId : sessionIdSet) { //迭代Key集合
Session session = clientMap.get(sessionId); //根據(jù)Key得到value
session.getAsyncRemote().sendText(message); //發(fā)送消息給客戶(hù)端
}
}
}
二、與WebSocket服務(wù)建立連接(Vue前端)
WebSocket是js自帶的一個(gè)對(duì)象,所以此處不需要任何引入第三方依賴(lài)包的操作。
WebSocket對(duì)象講解:
創(chuàng)建WebSocket對(duì)象
const ws = new WebSocket('ws://127.0.0.1:8088/websocket') // WebSocket服務(wù)的建立需要使用ws協(xié)議或者wss協(xié)議
onopen事件監(jiān)聽(tīng)
// 建立連接后的回調(diào)函數(shù) openCallback(e){ console.log('與服務(wù)端連接打開(kāi)->',e) }
onerror事件監(jiān)聽(tīng)
// 連接異常后的回調(diào)函數(shù) errorCallback(e){ console.log('與服務(wù)端連接打開(kāi)->',e) }
onclose事件監(jiān)聽(tīng)
// 關(guān)閉連接的回調(diào)函數(shù) closeCallback(e){ console.log('與服務(wù)端連接打開(kāi)->',e) }
onmessage事件監(jiān)聽(tīng)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-852288.html
// 接收到服務(wù)端的回信后的回調(diào)函數(shù) messageCallback(e){ console.log('與服務(wù)端連接打開(kāi)->',e) }
1、包裝后的webSocket.js
/**
* 參數(shù)說(shuō)明:
* webSocketURL:String webSocket服務(wù)地址 eg: ws://127.0.0.1:8088/websocket (后端接口若為restful風(fēng)格可以帶參數(shù))
* callback:為帶一個(gè)參數(shù)的回調(diào)函數(shù)
* message:String 要傳遞的參數(shù)值(不是一個(gè)必要的參數(shù))
*/
export default{
// 初始化webSocket
webSocketInit(webSocketURL){ // ws://127.0.0.1:8088/websocket
this.webSocket = new WebSocket(webSocketURL);
this.webSocket.onopen = this.onOpenwellback;
this.webSocket.onmessage = this.onMessageCallback;
this.webSocket.onerror = this.onErrorCallback;
this.webSocket.onclose = this.onCloseCallback;
},
// 自定義回調(diào)函數(shù)
setOpenCallback(callback){ // 與服務(wù)端連接打開(kāi)回調(diào)函數(shù)
this.webSocket.onopen = callback;
},
setMessageCallback(callback){ // 與服務(wù)端發(fā)送消息回調(diào)函數(shù)
this.webSocket.onmessage = callback;
},
setErrorCallback(callback){ // 與服務(wù)端連接異常回調(diào)函數(shù)
this.webSocket.onerror = callback;
},
setCloseCallback(callback){ // 與服務(wù)端連接關(guān)閉回調(diào)函數(shù)
this.webSocket.onclose = callback;
},
close(){ // 關(guān)閉連接
this.webSocket.close();
},
sendMessage(message){ // 發(fā)送消息函數(shù)
this.webSocket.send(message);
},
}
2、Vue中WebSocket對(duì)象的使用
<template>
<el-button type="primary" @click="sendMessage">發(fā)送消息</el-button>
</template>
<script>
import webSocket from '@/api/evgis/webSocket'
export default {
name:"WebSocketTest",
data(){
return{
webSocketObject: null,
}
},
created() {
// webSocket.webSocketInit(process.env.VUE_APP_BASE_API.replace("http","ws")+"/evgis/todoStatus")
webSocket.webSocketInit('ws://127.0.0.1:8088/websocket') //初始化webSocket
// 按需進(jìn)行綁定回調(diào)函數(shù)
webSocket.setOpenCallback(res=>{
console.log("連接建立成功",res);
})
webSocket.setMessageCallback(res=>{
// 在此處進(jìn)行數(shù)據(jù)刷新操作即可實(shí)現(xiàn)數(shù)據(jù)發(fā)生改變時(shí)實(shí)時(shí)更新數(shù)據(jù)
console.log("接收到回信",res);
})
webSocket.setErrorCallback(res=>{
console.log("連接異常",res);
})
webSocket.setCloseCallback(res=>{
console.log("連接關(guān)閉",res);
})
},
methods:{
sendMessage(){
// 數(shù)據(jù)發(fā)生改變時(shí)給WebSocket發(fā)送消息,讓其進(jìn)行廣播操作
webSocket.sendMessage();
}
}
}
</script>
<style>
</style>
三、實(shí)踐時(shí)遇到困難
1、由于使用的時(shí)若依框架,配置好WebSocket服務(wù)后需要開(kāi)放出ws的服務(wù)地址,否則會(huì)提示未帶token,WebSocket連接不上。
2、在配置WebSocket服務(wù)時(shí),沒(méi)有在關(guān)閉連接方法中移除連接對(duì)象。導(dǎo)致建立WebSocket連接后一發(fā)送消息就斷開(kāi)連接。
參考文章:前后端使用利用WebSocket進(jìn)行通信、服務(wù)器推送消息到前端實(shí)現(xiàn)頁(yè)面數(shù)據(jù)實(shí)時(shí)刷新-分布式Websocket技術(shù)方案文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-852288.html
到了這里,關(guān)于WebSocket實(shí)現(xiàn)后端數(shù)據(jù)變化,通知前端實(shí)時(shí)更新數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!