1、后端引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.1.13.RELEASE</version>
</dependency>
2、引入websocket配置
WebSocketConfig.java
/**
* @auther pxm
* @date 2023/6/16
* @description
*/
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 开启WebSocket支持
* @author drg
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocketServer.java(/websocket/** 方法需要加入白名单)
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.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* websocket 服务端
*
* @author pxm
* @return
* @date 2023/9/12 9:25
*/
@Slf4j
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketServer {
/**
* 记录当前在线连接数
*/
private static AtomicInteger onlineCount = new AtomicInteger(0);
/**
* 存放所有在线的客户端 key 为用户Id
*/
private static Map<String, Session> clients = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
if (clients.containsKey(userId)) {
clients.remove(userId);
clients.put(userId, session);
} else {
onlineCount.incrementAndGet(); // 在线数加1
clients.put(userId, session);
//sendMessage("客户端"+userId,session);
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
for (String userId : clients.keySet()) {
if (clients.get(userId).equals(session)) {
clients.remove(session);
onlineCount.decrementAndGet(); // 在线数减1
}
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message);
//this.sendMessage(message, session);
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 群发消息
*
* @param message 消息内容
*/
public void sendMessage(String message, Session fromSession) {
for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
Session toSession = sessionEntry.getValue();
// 排除掉自己
//if (!fromSession.getId().equals(toSession.getId())) {
log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
toSession.getAsyncRemote().sendText(message);
//}
}
}
/**
* 群发消息
*
* @param message 消息内容
*/
public static void sendMessage(String message, String id) {
Session session = clients.get(id);
if (session != null) {
log.debug("服务端给客户端[{}]发送消息{}", session.getId(), message);
try {
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
log.debug("数据发送失败!疑似断开连接", session.getId(), message);
clients.remove(id);
}
}
}
/**
* 根据用户id获取session
*
* @param userId
* @return
*/
public Session getUserSession(String userId) {
return clients.get(userId);
}
}
其中userId参数表示唯一标识符,可以自己定义,用于前端校验
WebSocketUsersjava
import javax.websocket.Session;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* websocket 客户端用户集
*
* @author ruoyi
*/
public class WebSocketUsers
{
/**
* 用户集
*/
private static Map<String, Session> USERS = new ConcurrentHashMap<String, Session>();
/**
* 存储用户
*
* @param key 唯一键
* @param session 用户信息
*/
public static void put(String key, Session session)
{
USERS.put(key, session);
}
/**
* 移除用户
*
* @param session 用户信息
*
* @return 移除结果
*/
public static boolean remove(Session session)
{
String key = null;
boolean flag = USERS.containsValue(session);
if (flag)
{
Set<Map.Entry<String, Session>> entries = USERS.entrySet();
for (Map.Entry<String, Session> entry : entries)
{
Session value = entry.getValue();
if (value.equals(session))
{
key = entry.getKey();
break;
}
}
}
else
{
return true;
}
return remove(key);
}
/**
* 移出用户
*
* @param key 键
*/
public static boolean remove(String key)
{
Session remove = USERS.remove(key);
if (remove != null)
{
boolean containsValue = USERS.containsValue(remove);
return containsValue;
}
else
{
return true;
}
}
/**
* 获取在线用户列表
*
* @return 返回用户集合
*/
public static Map<String, Session> getUsers()
{
return USERS;
}
/**
* 群发消息文本消息
*
* @param message 消息内容
*/
public static void sendMessageToUsersByText(String message)
{
Collection<Session> values = USERS.values();
for (Session value : values)
{
sendMessageToUserByText(value, message);
}
}
/**
* 发送文本消息
* @param message 消息内容
*/
public static void sendMessageToUserByText(Session session, String message)
{
if (session != null)
{
try
{
session.getBasicRemote().sendText(message);
}
catch (IOException e)
{
// LOGGER.error("\n[发送消息异常]", e);
}
}
else
{
// LOGGER.info("\n[你已离线]");
}
}
3、发送websocket(传入要发送的消息以及唯一标识)
WebSocketServer.sendMessage("message",yourId);
4、vue页面
在create中舒适化websocket(传入与后端一致的唯一标识)
created() {
//初始化websocket
this.initWebSocket(this.$route.query.examId);
},
在method中写相关方法
/****************************websocker ****************************************/
/**
* @description: 初始化WebSocket连接
* @author: pxm
* @modify:
* @return {*}
*/
initWebSocket(examId) {
// 定义websocket的地址 自行更改为自己后端的ip和端口
const wsuri = "ws://ip:port/websocket/" + examId;
// 判断当前浏览器是否支持websocket
if (typeof WebSocket == "undefined") {
console.log("您的浏览器不支持WebSocket");
} else {
console.log(wsuri);
// 创建websocket实例
this.websock = new WebSocket(wsuri);
// 监听websocket的message事件
this.websock.onmessage = this.websocketonmessage;
// 监听websocket的open事件
this.websock.onopen = this.websocketonopen;
// 监听websocket的error事件
this.websock.onerror = this.websocketonerror;
// 监听websocket的close事件
this.websock.onclose = this.websocketclose;
}
},
/**
* @description: 连接建立之后执行send方法发送数据
* @author: pxm
* @modify:
* @return {*}
*/
websocketonopen() {
// 定义一个actions对象,用于存储测试信息
let actions = { test: "我已在线" };
// 将actions对象转换成JSON字符串,并发送到websocket
this.websocketsend(JSON.stringify(actions));
},
/**
* @description: 连接建立失败重连
* @author: pxm
* @modify:
* @return {*}
*/
websocketonerror() {
// 重新初始化
this.initWebSocket(this.$route.query.examId);
},
/**
* @description: 数据接收(接到请求后,需要后续做的事)
* @author: pxm
* @modify:
* @return {*}
*/
websocketonmessage(e) {
console.log(e.data);
if (e.data) {
//根据自己逻辑完善监听后功能
doyourmethod();
}
},
/**
* @description: 数据发送
* @author: pxm
* @modify:
* @param {*} Data 发送数据
* @return {*}
*/
websocketsend(Data) {
this.websock.send(Data);
},
/**
* @description: 关闭
* @author: pxm
* @modify:
* @param {*} e 信息
* @return {*}
*/
websocketclose(e) {
console.log("断开连接", e);
},