背景
项目中需要做一个消息提示功能,当有用户处理相关待办信息后,别的用户需要实时更新处理后的待办信息。
解决方案:
1、使用最原始的方法,写个定时器去查询待办信息。但这种方式在大多数情况是不被允许的,它会浪费系统中的许多资源,同时也并不是完全意义上的实时更新。
2、使用WebSocket通信技术去实现一个实时更新,它可以实现广播和私信的模式。当一个用户与WebSocket服务建立连接后,用户可以给它发送一个消息,此时WebSocket服务会接收到这个消息并做出回信(此时可以回信给所有与其建立连接的用户——广播,也可以回信给指定用户——私信)。接下来将从前后端去讲解WebSocket的使用。
一、WebSocket服务的搭建(SpringBoot后端)
SpringBoot自带的WebSocket有以下5个注解需要注意:
- @ServerEndpoint暴露出的ws应用的路径,支持RESTful风格传参,类似/websocket/{username}
- @OnOpen与当前客户端连接成功,有入参Session对象(当前连接对象),同时可以利用@PathParam()获取上述应用路径中传递的参数,比如@PathParam(“username”) String username。
- @OnClose与当前客户端连接失败,有入参Session对象(当前连接对象),同时也可以利用@PathParam()获取上述应用路径中传递的参数。
- @OnError与当前客户端连接异常,有入参Session对象(当前连接对象)、Throwable对象(异常对象),同时也可以利用@PathParam()获取上述应用路径中传递的参数。
- @OnMessage当前客户端发送消息,有入参Session对象(当前连接对象)、String message对象(当前客户端传递过来的字符串消息)
1、引入所需依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
2、使用自定义类开启WebSocket
importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.socket.config.annotation.EnableWebSocket;importorg.springframework.web.socket.server.standard.ServerEndpointExporter;/**
* @program: webSocketTest
* @description: WebSocket相关配置
* @author: 黄珺瑜
* @create: 2022-06-30 16:24
**/@Configuration@EnableWebSocketpublicclassWebSocketConfig{@BeanpublicServerEndpointExporterserverEndpointExporter(){returnnewServerEndpointExporter();}}
3、配置WebSocket服务
importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjavax.websocket.*;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.Map;importjava.util.Set;importjava.util.concurrent.ConcurrentHashMap;/**
* @program: webSocketTest
* @description: WebSocket服务
* @author: 黄珺瑜
* @create: 2022-06-30 16:25
**/@Component@Slf4j@ServerEndpoint("/websocket")//暴露的ws应用的路径publicclassWebSocket{// 用来存储服务连接对象privatestaticMap<String,Session> clientMap =newConcurrentHashMap<>();/**
* 客户端与服务端连接成功
* @param session
*/@OnOpenpublicvoidonOpen(Session session){/*
do something for onOpen
与当前客户端连接成功时
*/
clientMap.put(session.getId(),session);}/**
* 客户端与服务端连接关闭
* @param session
*/@OnClosepublicvoidonClose(Session session){/*
do something for onClose
与当前客户端连接关闭时
*/
clientMap.remove(session.getId());}/**
* 客户端与服务端连接异常
* @param error
* @param session
*/@OnErrorpublicvoidonError(Throwable error,Session session){
error.printStackTrace();}/**
* 客户端向服务端发送消息
* @param message
* @throws IOException
*/@OnMessagepublicvoidonMsg(Session session,String message)throwsIOException{/*
do something for onMessage
收到来自当前客户端的消息时
*/sendAllMessage(message);}//向所有客户端发送消息(广播)privatevoidsendAllMessage(String message){Set<String> sessionIdSet = clientMap.keySet();//获得Map的Key的集合// 此处相当于一个广播操作for(String sessionId : sessionIdSet){//迭代Key集合Session session = clientMap.get(sessionId);//根据Key得到value
session.getAsyncRemote().sendText(message);//发送消息给客户端}}}
二、与WebSocket服务建立连接(Vue前端)
WebSocket是js自带的一个对象,所以此处不需要任何引入第三方依赖包的操作。
WebSocket对象讲解:
- 创建WebSocket对象
const ws =newWebSocket('ws://127.0.0.1:8088/websocket')// WebSocket服务的建立需要使用ws协议或者wss协议
- onopen事件监听
// 建立连接后的回调函数openCallback(e){ console.log('与服务端连接打开->',e)}
- onerror事件监听
// 连接异常后的回调函数errorCallback(e){ console.log('与服务端连接打开->',e)}
- onclose事件监听
// 关闭连接的回调函数closeCallback(e){ console.log('与服务端连接打开->',e)}
- onmessage事件监听
// 接收到服务端的回信后的回调函数messageCallback(e){ console.log('与服务端连接打开->',e)}
1、包装后的webSocket.js
/**
* 参数说明:
* webSocketURL:String webSocket服务地址 eg: ws://127.0.0.1:8088/websocket (后端接口若为restful风格可以带参数)
* callback:为带一个参数的回调函数
* message:String 要传递的参数值(不是一个必要的参数)
*/exportdefault{// 初始化webSocketwebSocketInit(webSocketURL){// ws://127.0.0.1:8088/websocketthis.webSocket =newWebSocket(webSocketURL);this.webSocket.onopen =this.onOpenwellback;this.webSocket.onmessage =this.onMessageCallback;this.webSocket.onerror =this.onErrorCallback;this.webSocket.onclose =this.onCloseCallback;},// 自定义回调函数setOpenCallback(callback){// 与服务端连接打开回调函数this.webSocket.onopen = callback;},setMessageCallback(callback){// 与服务端发送消息回调函数this.webSocket.onmessage = callback;},setErrorCallback(callback){// 与服务端连接异常回调函数this.webSocket.onerror = callback;},setCloseCallback(callback){// 与服务端连接关闭回调函数this.webSocket.onclose = callback;},close(){// 关闭连接this.webSocket.close();},sendMessage(message){// 发送消息函数this.webSocket.send(message);},}
2、Vue中WebSocket对象的使用
<template>
<el-button type="primary" @click="sendMessage">发送消息</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
// 按需进行绑定回调函数
webSocket.setOpenCallback(res=>{
console.log("连接建立成功",res);
})
webSocket.setMessageCallback(res=>{
// 在此处进行数据刷新操作即可实现数据发生改变时实时更新数据
console.log("接收到回信",res);
})
webSocket.setErrorCallback(res=>{
console.log("连接异常",res);
})
webSocket.setCloseCallback(res=>{
console.log("连接关闭",res);
})
},
methods:{
sendMessage(){
// 数据发生改变时给WebSocket发送消息,让其进行广播操作
webSocket.sendMessage();
}
}
}
</script>
<style>
</style>
三、实践时遇到困难
1、由于使用的时若依框架,配置好WebSocket服务后需要开放出ws的服务地址,否则会提示未带token,WebSocket连接不上。
2、在配置WebSocket服务时,没有在关闭连接方法中移除连接对象。导致建立WebSocket连接后一发送消息就断开连接。
参考文章:前后端使用利用WebSocket进行通信、服务器推送消息到前端实现页面数据实时刷新-分布式Websocket技术方案
版权归原作者 余鱼瑜渔 所有, 如有侵权,请联系我们删除。