1、websocket 推送 依赖引入
org.springframework.boot
spring-boot-starter-websocket
2、添加消息配置,新建WebSocketConfig .java文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @ClassName WebSocketConfig
* @Description WebSocket 消息配置
* @Author 孜然公子
* @Date 2022年11月2日16:50:49
*/
@Configuration
public class WebSocketConfig {
/**
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3、配置封装websocket组件,处理连接、消息发送相关。新建WebSocketServer.java文件。
import com.alibaba.fastjson.JSON;
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.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName WebSocketConfig
* @Description WebSocket 消息配置 接口路径 ws://ip:port/webSocket/userId;
* @Author 孜然公子
* @Date 2022年11月2日16:50:49
*/
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
// 非活动连接保持时间
private static final long sessionTimeout = 60000;
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private static Session session;
// 用来存在线连接数
private static final Map sessionPool = new ConcurrentHashMap<>();
/**
* 链接成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "userId") String userId) {
try {
session.setMaxIdleTimeout(sessionTimeout);
this.session = session;
synchronized (sessionPool) {
if (sessionPool.containsKey(userId)) {
sessionPool.remove(userId);
}
sessionPool.put(userId, this);
}
// log.info("【websocket服务端】有新的连接,总数为:" + sessionPool.size());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 链接关闭调用的方法
*/
@OnClose
public void onClose(Session session) {
try {
for (Map.Entry entry : sessionPool.entrySet()) {
String mapKey = entry.getKey();
if (entry.getValue() != null && entry.getValue().session.equals(session)) {
sessionPool.remove(mapKey);
}
}
// log.info("【websocket服务端】连接断开,当前连接总数为:" + sessionPool.size());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 收到客户端消息后调用的方法
*/
@OnMessage
public void onMessage(Session session, String message) {
// log.info("【websocket服务端】收到客户端消息:" + message);
if (("HeartBeat").equals(message)) {
try {
sendMessage(session, "心跳");
} catch (Exception e) {
for (Map.Entry entry : sessionPool.entrySet()) {
String mapKey = entry.getKey();
if (entry.getValue() != null && entry.getValue().session.equals(session)) {
sessionPool.remove(mapKey);
}
}
}
}
}
/**
* 发送错误时的处理
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 此为广播消息
*/
public void sendAllMessage(String message) {
// log.info("【websocket服务端】广播消息:" + message);
sessionPool.forEach((k, v) -> {
try {
if (v != null && v.session != null && v.session.isOpen()) {
sendMessage(v.session, message);
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* 此为单点消息
*/
public void sendOneMessage(String userId, String message) {
WebSocketServer webSocketServer = sessionPool.get(userId);
if (webSocketServer != null) {
Session pSession = webSocketServer.session;
if (pSession != null && pSession.isOpen()) {
try {
// log.info("【websocket服务端】 单点消息:" + message);
sendMessage(pSession, message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 此为单点消息(多人)
*/
public void sendMoreMessage(String[] userIds, String message) {
for (String userId : userIds) {
WebSocketServer webSocketServer = sessionPool.get(userId);
if (webSocketServer != null) {
Session pSession = webSocketServer.session;
if (pSession != null && pSession.isOpen()) {
try {
// log.info("【websocket服务端】 单点消息:" + message);
sendMessage(pSession, message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 此为单点消息(多人)
*/
public void sendMoreMessage(List userIds, String message) {
// System.out.println("【websocket服务端】 单点消息:" + message + " 目标人群:" + JSON.toJSONString(userIds));
// System.out.println("【websocket服务端】 会话列表:" + JSON.toJSONString(sessionPool.keySet()));
for (String userId : userIds) {
WebSocketServer webSocketServer = sessionPool.get(userId);
if (webSocketServer != null) {
Session pSession = webSocketServer.session;
if (pSession != null && pSession.isOpen()) {
try {
// System.out.println("【websocket服务端】 单点消息:" + userId + " " + message);
sendMessage(pSession, message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
/**
* 服务端群发消息-心跳包
*
* @param message 消息
* @return int
*/
public static synchronized int sendPing(String message) {
if (sessionPool.size() <= 0) {
return 0;
}
StringBuffer userIds = new StringBuffer();
AtomicInteger count = new AtomicInteger();
sessionPool.forEach((userId, server) -> {
count.getAndIncrement();
if (sessionPool.containsKey(userId)) {
try {
if (server != null) {
Session pSession = server.session;
if (pSession != null && pSession.isOpen()) {
sendMessage(pSession, message);
if (count.equals(sessionPool.size() - 1)) {
userIds.append(userId);
} else {
userIds.append(userId).append(",");
}
} else {
sessionPool.remove(userId);
}
}
} catch (Exception e) {
sessionPool.remove(userId);
// System.out.println("【websocket服务端】客户端心跳检测异常:" + e.getMessage());
log.info("【websocket服务端】客户端心跳检测异常移除: " + userId + ",心跳发送失败,已移除!");
}
} else {
log.info("【websocket服务端】客户端心跳检测异常不存在: " + userId + ",不存在!");
}
});
log.info("【websocket服务端】客户端心跳检测结果: " + userIds + "连接正在运行");
return sessionPool.size();
}
/**
* 发送消息
*
* @param session 会话连接
* @param message 消息内容
*/
public synchronized static void sendMessage(Session session, String message) throws IOException {
session.getBasicRemote().sendText(message);
}
}
4、如果后台发送心跳检测消息,则创建WebSocketScheduleTask.java文件
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @ClassName websocket 任务
* @Description
* @Author 孜然公子
* @Date 2022/11/12 18:00
*/
@Component
@Slf4j
@EnableScheduling
public class WebSocketScheduleTask {
/**
* 每10s发送一次心跳给客户端
*/
@Scheduled(cron = "0/10 * * * * ?")
public void sendPingToClient() {
log.info("【websocket服务端】websocket心跳开始发送");
int num = 0;
try {
num = WebSocketServer.sendPing("心跳");
} finally {
log.info("【websocket服务端】websocket心跳检测结果,共【" + num + "】个连接");
}
log.info("【websocket服务端】websocket心跳发送结束");
}
}
注:如果启动文件Application中已经添加注解@EnableScheduling,该文件中就不要再添加该注解了!!!
5、对外ws协议消息配置接口路径地址为 ws://ip:port/webSocket/userId
感谢浏览Thanks♪(・ω・)ノ