前言:
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:java专栏
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏
目录
- 一.环境准备
- 二.vue代码
- 三.SpringWebsocket相关理论
- 3.1 AbstractWebSocketHandler
- 3.2 HttpSessionHandshakeInterceptor
- 3.3 WebSocketConfigurer
- 四.spring boot代码
- 4.1继承AbstractWebSocketHandler
- 4.2 实现WebSocketConfigurer
一.环境准备
vue3 :官方文档 https://cn.vuejs.org/
pinia:官方文档 https://pinia.vuejs.org/zh/
springWebSocket的maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二.vue代码
pinia的安装和使用就不说了直接看官方文档就行。
1、创建stores/demo.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useChantStore = defineStore('chant', () => {
//state
const msg = ref("")//发送的消息
var socket= ref() ;
const userId = ref("") //传递的参数
const count = ref(0) //链接标志
//action
// Websoket连接成功事件
const websocketonopen = (res: any) => {
console.log("WebSocket连接成功", res);
};
// Websoket接收消息事件
const websocketonmessage = (res: any) => {
console.log("数据", res);
msg.value = res.data
};
// Websoket连接错误事件
const websocketonerror = (res: any) => {
console.log("连接错误", res);
};
// Websoket断开事件
const websocketclose = (res: any) => {
console.log("断开连接", res);
websocketclose;
// 销毁 websocket 实例对象
socket.value = null;
count.value = 0;
userId.value = '';
};
//创建链接
const connectWebSocket = () =>{
console.log("websocket创建链接 usrid= ",userId.value);
const wsurl = `ws://127.0.0.1:8888/api/myWs1?userId=${userId.value}`;
socket.value = new WebSocket(wsurl);
socket.value.onopen = websocketonopen;
socket.value.onmessage = websocketonmessage;
socket.value.onerror = websocketonerror;
socket.value.onclose = websocketclose;
count.value = 1;
}
//关闭链接
const closetWebSocket = () =>{
websocketclose;
// 销毁 websocket 实例对象
socket.value = null;
count.value = 0;
userId.value = '';
}
return { msg, socket,userId,count,connectWebSocket,closetWebSocket }
})
二、在使用的组件中使用
import { useChantStore } from '../stores/chant'
//websocket
const chantWebSocket = useChantStore()
//你自己的代码
三.SpringWebsocket相关理论
主要的接口
3.1 AbstractWebSocketHandler
AbstractWebSocketHandler
:是 Spring Framework 中用于处理 WebSocket 通信的一个基础抽象类,它位于 org.springframework.web.socket.handler 包下。在构建基于Spring的WebSocket应用时,开发者可以扩展这个类来创建自定义的消息处理器。
-
连接建立与关闭处理
void afterConnectionEstablished(WebSocketSession session)
:当一个新的WebSocket连接建立后,此方法会被调用,开发者可以在其中进行初始化操作或者订阅事件。void handleTransportError(WebSocketSession session, Throwable exception)
:当在传输层发生错误时调用,可用于处理异常情况和清理资源。void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
:在WebSocket连接关闭后执行,通常用来清理资源或记录日志。
-
消息处理
void handleMessage(WebSocketSession session, WebSocketMessage<?> message)
:这是一个抽象方法,子类需要实现它来处理从客户端接收到的各种类型的消息。 -
握手协商:
可以通过重写
boolean supportsPartialMessages()
来表明是否支持分片消息。 -
其他功能:
它还可能提供一些便利的方法用于发送消息给客户端、设置会话属性等。
3.2 HttpSessionHandshakeInterceptor
HttpSessionHandshakeInterceptor
是 Spring Framework 中用于 WebSocket 扩展的一个类,它继承自 HandshakeInterceptor 接口,并特别关注于在 WebSocket 握手阶段与 HTTP HttpSession 的交互。
当客户端尝试建立 WebSocket 连接时,会触发一个握手过程,在这个过程中,服务器可以使用 HttpSessionHandshakeInterceptor 来拦截握手请求和响应,以便进行额外的检查、修改握手参数或执行特定的业务逻辑。
通过实现或扩展 HttpSessionHandshakeInterceptor 类,开发人员能够定制WebSocket连接初始化的过程,比如进行权限验证、会话同步以及对跨域支持等需求的处理。
HttpSessionHandshakeInterceptor 提供了两个核心方法
-
beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes):
- 在握手开始前调用,允许开发者检查HTTP请求并决定是否应该继续握手。
- 可以从HTTP请求中获取当前的HttpSession,并将需要的数据复制到WebSocketSession的属性中。
- 可以通过修改attributes参数来添加或删除即将传递给WebSocketSession的属性。
- 如果返回false,则会中断握手,即拒绝建立WebSocket连接。
-
afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception):
- 在握手成功完成后调用,无论握手是否成功,都会执行此方法(如果握手失败,则exception参数将包含相关异常)。
- 通常在此处用于清理资源、记录日志或者进行其他后处理操作。
3.3 WebSocketConfigurer
WebSocketConfigurer
是 Spring Framework 中用于配置 WebSocket 功能的接口,它允许开发者自定义 WebSocket 服务端点(endpoint)的行为和路由规则。该接口位于 org.springframework.web.socket.config.annotation 包下,是 Spring WebSocket 支持的核心部分。
在基于 Spring Boot 构建 WebSocket 应用程序时,通过实现 WebSocketConfigurer 接口或使用其子接口如 DelegatingWebSocketMessageBrokerConfiguration 等,可以方便地配置 WebSocket 的核心功能:
-
注册 WebSocketHandler:
开发者可以通过实现
WebSocketConfigurer
中的相应方法来注册处理 WebSocket 连接请求的 WebSocketHandler 实例。这些 Handler 负责与客户端进行双向通信,包括消息接收、处理和发送。 -
配置访问路径:
可以设置 WebSocket 的入口地址,即客户端通过哪个 URL 建立 WebSocket 连接。
-
跨域支持:
配置是否允许来自不同源的 WebSocket 请求,即 CORS (Cross-Origin Resource Sharing) 政策。
-
定制握手过程:
添加
HandshakeInterceptor
实例,以便在 WebSocket 握手阶段执行额外的验证逻辑或操作。 -
会话管理:
定义如何管理 WebSocketSession,例如设置超时策略等。
四.spring boot代码
4.1继承AbstractWebSocketHandler
@Slf4j
@Component
public class MyWsHandler extends AbstractWebSocketHandler {
//保存已经链接的用户
private static Map<String, WsSessionBean> wsSessionBeanMap;
static {
wsSessionBeanMap = new ConcurrentHashMap<>();
}
/**
* //建立链接
* @param session
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
//过去ur中的参数
URI uri = session.getUri();
UriComponents components = UriComponentsBuilder.fromUri(uri).build();
MultiValueMap<String, String> queryParams = components.getQueryParams();
String userId = queryParams.getFirst("userId");
WsSessionBean wsSessionBean = new WsSessionBean(Integer.valueOf(userId), session);
wsSessionBeanMap.put(session.getId(), wsSessionBean);
log.info("建立链接了。。。。。{}", wsSessionBean);
}
//收到消息
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
super.handleMessage(session, message);
log.info("当前session信息:{}", session);
log.info("消息是:message:{}", message.getPayload());
//广播消息===========================自己实现的代码,用来发送消息
sendMessageToBroadcast(session.getId(), message.getPayload().toString());
}
/**
* 发送消息到所有客户端 (广播消息 发给当前用户得所有好友)
*
* @param userId 当前用户sessionId
* @param messageText 当前用户要发送得内容
*/
public void sendMessageToBroadcast(String userId, String messageText) {
wsSessionBeanMap.forEach((k, v) -> {
if (!k.equals(userId)) {//说明要发得对象
WebSocketSession targetSession = v.getSession();
if (targetSession != null && targetSession.isOpen()) {
TextMessage textMessage = new TextMessage(messageText);
try {
targetSession.sendMessage(textMessage);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
log.info("无法向用户{}发送消息,会话未建立或已关闭", userId);
}
}
});
}
/**
* 传输异常
*
* @param session
* @param exception
* @throws Exception
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
super.handleTransportError(session, exception);
if (session.isOpen()) {
session.close();
wsSessionBeanMap.remove(session.getId());
}
log.info("传输异常");
}
/**
* 链接关闭
*
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
wsSessionBeanMap.remove(session.getId());
log.info("链接关闭");
}
}
4.2 实现WebSocketConfigurer
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private MyWsHandler myWsHandler;
@Bean
public HandshakeInterceptor httpSessionHandshakeInterceptor() {
return new HttpSessionHandshakeInterceptor();
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWsHandler,"/myWs1")
.setAllowedOrigins("*");
}
}
欢迎大家👍点赞👍 + 👀关注👀 + 🤏收藏🤏 如有不足请指正