后端 WebSocketServer.java 文件:
package com.ruoyi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@Service
@ServerEndpoint("/websocket/{username}")
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
// 当前在线连接数。
private static AtomicInteger onlineCount = new AtomicInteger(0);
// 存放每个客户端对应的WebSocket对象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//接收sid
private String username = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
this.session = session;
webSocketSet.add(this);
this.username = username;
addOnlineCount();
try {
log.info("连接成功,有新客户端开始监听,username=" + username + ",当前在线人数为:" + getOnlineCount());
sendMessage(this.username + "连接成功", username);
} catch (Exception e) {
log.error("websocket IO Exception");
e.printStackTrace();
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); // 从set中删除
subOnlineCount(); // 在线数减1
// 断开连接情况下,更新主板占用情况为释放
log.info("释放的username=" + username + "的客户端");
releaseResource();
}
/**
* 收到客户端消息后调用的方法
*
* @Param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自客户端 username=" + username + " 的信息:" + message);
// 群发消息
HashSet<String> userList = new HashSet<>();
for (WebSocketServer item : webSocketSet) {
userList.add(item.username);
}
try {
sendMessage("客户端 " + this.username + "发布消息:" + message, userList);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发生错误回调
*/
@OnError
public void onError(Session session, Throwable error) {
log.error(session.getBasicRemote() + "客户端发生错误");
error.printStackTrace();
}
/**
* 群发送消息
*/
public static void sendMessage(String message, HashSet<String> userList) throws IOException {
log.info("推送消息到客户端 " + userList + ",推送内容:" + message);
for (WebSocketServer item : webSocketSet) {
try {
if (userList.contains(item.username)) {
item.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 发送消息
*/
public static void sendMessage(String message, String username) {
log.info("推送消息到客户端 " + username + ",推送内容:" + message);
try {
for (WebSocketServer item : webSocketSet) {
if (username.contains(item.username)) {
item.sendMessage(message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 实现服务器主动推送消息到 指定客户端
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 获取当前在线人数
*
* @return
*/
public static int getOnlineCount() {
return onlineCount.get();
}
/**
* 当前在线人数 +1
*
* @return
*/
public static void addOnlineCount() {
onlineCount.getAndIncrement();
}
/**
* 当前在线人数 -1
*
* @return
*/
public static void subOnlineCount() {
onlineCount.getAndDecrement();
}
/**
* 获取当前在线客户端对应的WebSocket对象
*
* @return
*/
public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
return webSocketSet;
}
private void releaseResource() {
// 这里写释放资源和要处理的业务
log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
}
前端对接 WebSocket.vue 文件(Vue3):
<template>
<div>{{ messageNumber }}</div>
</template>
<script setup>
import { ref } from "vue";
const wsIsRun = ref(false);
const webSocket = ref(null);
const ws = ref("ws://127.0.0.1:19090/websocket/用户名");
const wsTimer = ref(null);
const messageNumber = ref(0); // 返回数量
onMounted(() => {
messageNumber.value = 0;
wsIsRun.value = true;
wsInit();
})
/***************webSocket****************/
const sendDataToServer = () => {
if (this.webSocket.readyState === 1) {
webSocket.value.send('来自前端的数据')
} else {
throw Error('服务未连接')
}
}
// 初始化ws
const wsInit = () => {
if (!wsIsRun.value) { return }
// 销毁ws
wsDestroy()
// 初始化ws
webSocket.value = new WebSocket(ws.value)
// ws连接建立时触发
webSocket.value.addEventListener('open', wsOpenHanler)
// ws服务端给客户端推送消息
webSocket.value.addEventListener('message', wsMessageHanler)
// ws通信发生错误时触发
webSocket.value.addEventListener('error', wsErrorHanler)
// ws关闭时触发
webSocket.value.addEventListener('close', wsCloseHanler)
// 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开
clearInterval(wsTimer.value)
wsTimer.value = setInterval(() => {
if (webSocket.value.readyState === 1) {
clearInterval(wsTimer.value)
} else {
console.log('ws建立连接失败')
wsInit()
}
}, 3000)
}
// ws连接建立时触发
const wsOpenHanler = (event) => {
console.log('ws建立连接成功')
}
// ws服务端给客户端推送消息
const wsMessageHanler = (event) => {
console.log('wsMessageHanler')
console.log(event)
if(event.data != null && event.data!= 0 && !isNaN(event.data)) {
messageNumber.value = event.data
}
}
// ws通信发生错误
const wsErrorHanler = (event) => {
console.log(event, 'ws通信发生错误')
wsInit()
}
// ws关闭
const wsCloseHanler = (event) => {
console.log(event, 'ws关闭')
wsInit()
}
// 销毁ws
const wsDestroy = () => {
if (webSocket.value !== null) {
webSocket.value.removeEventListener('open', wsOpenHanler)
webSocket.value.removeEventListener('message', wsMessageHanler)
webSocket.value.removeEventListener('error', wsErrorHanler)
webSocket.value.removeEventListener('close', wsCloseHanler)
webSocket.value.close()
webSocket.value = null
clearInterval(wsTimer.value)
}
}
</script>
<style scoped lang="scss">
</style>
通过对接地址 前端可根据监听获取后端发送的消息信息