目的:为了实现后续聊天室的功能,以及目前某一用户预约另一用户,用一用户需要即时收到消息提示的功能,使用webSocket双工通信实现
1.导入webSocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>${websocket.version}</version> //2.7.0
</dependency>
2.config下建立webSocket配置
package com.base.utils.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3.添加WebSocket类
对@ServerEndpoint("/noAuth/websocket/{userId}")的说明:理解为路径通用匹配字段,由前端声明前缀,相同路径的信息才可以相互传输。比如下面前端路径声明为ws://localhost:8180/noAuth/websocket/1 ,则表名该连接是与userId为1的用户连接,并且只有
localhost:8180发出的webSocket信息才能到达userId=1的socket接收器中。
package com.base.utils.common;
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.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@Slf4j
@ServerEndpoint("/noAuth/websocket/{userId}")
public class WebSocket {
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 用户ID
*/
private String userId;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
//虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
// 注:底下WebSocket是当前类名
private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();
// 用来存在线连接用户信息
private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();
/**
* 链接成功调用的方法,当前端与服务器端建立了连接,自动触发OnOpen注解方法
*里面的参数是固定的,userId来自于"/noAuth/websocket/{userId}"中的userId
*/
@OnOpen
public void onOpen(Session session, @PathParam(value="userId")String userId) {
try {
this.session = session;
this.userId = userId;
webSockets.add(this);
sessionPool.put(userId, session);
log.info("【websocket消息】有新的连接,用户Id:"+userId+ "总数为:"+webSockets.size());
} catch (Exception e) {
}
}
/**
* 链接关闭调用的方法
*/
@OnClose
public void onClose() {
try {
webSockets.remove(this);
sessionPool.remove(this.userId);
log.info("【websocket消息】连接断开,总数为:"+webSockets.size());
} catch (Exception e) {
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message
*/
@OnMessage
public void onMessage(String message) {
log.info("【websocket消息】收到客户端消息:"+message);
}
/** 发送错误时的处理
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误,原因:"+error.getMessage());
error.printStackTrace();
}
// 此为广播消息
public void sendAllMessage(String message) {
log.info("【websocket消息】广播消息:"+message);
for(WebSocket webSocket : webSockets) {
try {
if(webSocket.session.isOpen()) {
webSocket.session.getAsyncRemote().sendText(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息
public void sendOneMessage(String userId, String message) {
Session session = sessionPool.get(userId);
if (session != null&&session.isOpen()) {
try {
log.info("【websocket消息】 单点消息:"+message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息(多人)
public void sendMoreMessage(String[] userIds, String message) {
for(String userId:userIds) {
Session session = sessionPool.get(userId);
if (session != null&&session.isOpen()) {
try {
log.info("【websocket消息】 单点消息:"+message);
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
前端代码,建立连接
onMounted(() => {
let socket;
const ul = "ws://localhost:8180/noAuth/websocket/1"
socket = new WebSocket(ul);
socket.onopen = function(event) {
console.log("WebSocket连接成功!");
// 连接成功后的其他操作
};
});
4.测试实现效果:
前端页面启动,创建webSocket连接,此时是与后端服务为8180端口建立连接,触发8180的onOpen,控制台输出如下
在8180服务的controller中编写测试方法,测试广播发送,和单用户发送
@GetMapping("/noAuth/testWebSocket")
public void sendMsg(Long id){
JSONObject obj = new JSONObject();
obj.put("key1","value1");
obj.put("key2","value2");
webSocket.sendAllMessage(obj.toJSONString());
webSocket.sendOneMessage(Long.toString(id)),"he"+"预约了您,请尽快处理");
}
前端找到webSocket连接,在消息中查看接收到的内容,如果userId不匹配则接受不到
-------基础整合已实现,后续跟进预约时的应用以及聊天室的应用