<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
/**
* @author WeiDaPang
*/
@Configuration
public class ScheduledConfiguration{
@Bean
public TaskScheduler taskScheduler(){
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
return taskScheduler;
}
}
/**
* WebSocket 配置
* @author WeiDaPang
*/
@EnableWebSocket
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
/**
* 注入WebSocket的处理器
*/
@Resource
private WebSocketServer webSocketServer;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// webSocketDemo 为前端请求的地址,前端具体地址组成结构为:ws://ip:接口启动的端口/webSocketDemo
registry.addHandler(webSocketServer,"webSocketByNotice")
.setAllowedOrigins("*");
}
}
/**
* @author Da.Pang
* 2022/11/8 18:57
*/
public class WebSocketEntity {
private String userId;
private WebSocketSession webSocketSession;
public WebSocketEntity(String userId , WebSocketSession webSocketSession){
this.userId = userId;
this.webSocketSession = webSocketSession;
}
public String getUserId() {
return userId;
}
public WebSocketEntity setUserId(String userId) {
this.userId = userId;
return this;
}
public WebSocketSession getWebSocketSession() {
return webSocketSession;
}
public WebSocketEntity setWebSocketSession(WebSocketSession webSocketSession) {
this.webSocketSession = webSocketSession;
return this;
}
}
/**
* @author WeiDaPang
*/
@Component
public class WebSocketServer extends AbstractWebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(WsService.class);
@Resource
private WsService wsService;
/**
* 连接成功后触发
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("...[客户端连接成功]...");
// 客户端建立连接,将客户端的session对象加入到WebSocketSessionManager的sessionGroup中
WebSocketSessionManager.add(session);
//将连接结果返回给客户端
wsService.sendMsg(session,session.getId()+" 连接成功"+ LocalDateTime.now());
}
/**
* 关闭socket连接后触发
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
logger.info("...【客户端关闭连接成功】...");
// 关闭连接,从WebSocketSessionManager的sessionGroup中移除连接对象
WebSocketSessionManager.removeAndClose(session);
}
/**
* 接收客户端发送的文本消息
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.info("{ 客户端发送:}"+message.getPayload());
//将连接结果返回给客户端
wsService.sendMsg(session,message.getPayload());
}
/**
* 接收客户端发送的二进制消息
*/
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
logger.info("{ 客户端二进制发送: }"+message.getPayload());
}
/**
* 异常处理
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
System.out.println();
logger.error("*** socket异常: "+exception.getMessage(),exception);
// 出现异常则关闭连接,从WebSocketSessionManager的sessionGroup中移除连接对象
WebSocketSessionManager.removeAndClose(session);
}
}
/**
* @author WeiDaPang
*/
public class WebSocketSessionManager {
private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionManager.class);
/**
* 保存连接对象的 session 到集合中
*/
public static ConcurrentHashMap<String, WebSocketEntity> sessionGroup = new ConcurrentHashMap<>();
/**
* 添加 session 到集合中
* @param session 连接对象的session
*/
public static void add(WebSocketSession session) {
logger.info(String.valueOf(session.getUri()));
sessionGroup.put(session.getId(), new WebSocketEntity(getUserIdBySession(session), session));
}
/**
* 从集合中删除 session,会返回删除的 session
* @param key 通过session.getId()得到
*/
public static WebSocketSession remove(String key) {
return sessionGroup.remove(key).getWebSocketSession();
}
/**
* 从集合中删除 session,会返回删除的 session
* @param session 连接对象的session
*/
public static WebSocketSession remove(WebSocketSession session) {
return sessionGroup.remove(session.getId()).getWebSocketSession();
}
/**
* 删除并关闭 连接
* @param key 通过session.getId()得到
*/
public static void removeAndClose(String key) {
WebSocketSession session = remove(key);
if (session != null) {
try {
// 关闭连接
session.close();
} catch (IOException e) {
logger.error("关闭出现异常处理",e);
e.printStackTrace();
}
}
}
/**
* 删除并关闭 连接
* @param session 连接对象的session
*/
public static void removeAndClose(WebSocketSession session) {
WebSocketSession session2 = remove(session);
if (session2 != null) {
try {
// 关闭连接
session.close();
} catch (IOException e) {
logger.error("关闭出现异常处理",e);
e.printStackTrace();
}
}
}
/**
* 从集合中获得 session
* @param key 通过session.getId()得到
*/
public static WebSocketSession get(String key) {
return sessionGroup.get(key).getWebSocketSession();
}
/**
* [描述] 从ws地址中获取用户id
* 格式:ws://IP:端口/webSocketByNotice?userId=10000000
*/
public static String getUserIdBySession(WebSocketSession session){
String uri = Objects.requireNonNull(session.getUri()).toString();
uri = uri.substring(uri.indexOf("?")+1);
uri = uri.replace("userId=","").trim();
return uri;
}
}
/**
* @author WeiDaPang
*/
@Service
public class WsService {
/**
* 发送消息给指定客户端
* @param session 客户端session
* @param text 发送消息的内容
*/
public synchronized void sendMsg(WebSocketSession session, String text) throws IOException {
session.sendMessage(new TextMessage(text));
Notice notice = new Notice();
notice.setNoticeTitle("消息标题");
notice.setNoticeContent(text);
notice.setCreateTime(new Date());
// 发送一条消息 就广播给所有人
broadcastMsg(JSONUtil.toJsonStr(notice));
}
/**
* 发送消息给指定客户端
* @param session 客户端session
* @param notice 通知
*/
public synchronized void sendMsgByNotice(WebSocketSession session, Notice notice) throws IOException {
session.sendMessage(new TextMessage(JSONUtil.toJsonStr(notice)));
}
/**
* 发送消息给所有客户端,客户端的session必须在WebSocketSessionManager的sessionGroup中
* @param text 发送消息的内容
*/
public synchronized void broadcastMsg(String text) throws IOException {
for (WebSocketEntity entity: WebSocketSessionManager.sessionGroup.values()) {
entity.getWebSocketSession().sendMessage(new TextMessage(text));
}
}
}
DOCTYPE HTML>
<html lang="UTF-8">
<head>
<title>WebSocket 测试页面title>
head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息button>
<button onclick="closeWebSocket()">关闭webScoket连接button>
<div id="message">div>
body>
<script type="text/javascript">
// 连接到WebSocket的url地址。格式为 ws://ip:接口启动的端口/webSocketDemo
// 如果 sping boot 配置文件中中配置了server.servlet.context-path ,则格式为: ws://ip:接口启动的端口/server.servlet.context-path的名称/webSocketDemo
// 此处demo中 sping boot 配置了server.servlet.context-path 为 webSocketDemoApi
let connectionUrl = "ws://127.0.0.1:808/webSocketByNotice?userId=10000001"
let ws = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
ws = new WebSocket(connectionUrl);
}else {
alert('当前浏览器不支持 websocket')
}
//连接发生错误的回调方法
ws.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
};
//连接成功建立的回调方法
ws.onopen = function(event) {
console.log("ws调用连接成功回调方法")
//ws.send("")
}
//接收到消息的回调方法
ws.onmessage = function(message) {
console.log("接收消息:" + message.data);
if (typeof(message.data) == 'string') {
setMessageInnerHTML(message.data);
}
}
//ws连接断开的回调方法
ws.onclose = function(e) {
console.log("ws连接断开")
//console.log(e)
setMessageInnerHTML("ws close");
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
console.log(innerHTML)
document.getElementById('message').innerHTML += '接收的消息:' + innerHTML + '
';
}
//关闭连接
function closeWebSocket() {
ws.close();
}
//发送消息
function send(msg) {
if(!msg){
msg = document.getElementById('text').value;
console.log(msg)
document.getElementById('message').innerHTML += "发送的消息:" + msg + '
';
ws.send(msg);
}
}
script>
由于我的项目同时引入了
socket和定时任务,所以启动时候会报错,解决方式就是为socket创建一个【TaskScheduler】的bean。(第一个工具类就是解决这个问题的)