SpringBoot整合WebSocket
Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Websocket配置类
@Configuration
@EnableWebSocket
public class BootWebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpoint(){
return new ServerEndpointExporter ();
}
}
Websocket客户端心跳检测类
@Slf4j
@Component
public class BootWebSocketHeartbeat {
@Async
@Scheduled(cron = "0/5 * * * * ?")
public void validSession(){
ConcurrentHashMap<String, Long> webSocketHeartbeat = BootWebSocket.getWebSocketHeartbeat();
if (!CollectionUtils.isEmpty(webSocketHeartbeat)){
log.info("服务端检查客户端检查心跳...");
for (Map.Entry<String, Long> entry : webSocketHeartbeat.entrySet()) {
String sessionId = entry.getKey();
Long validTimeMillis = entry.getValue();
Long currTimeMillis = Long.valueOf(System.currentTimeMillis());
if (currTimeMillis.compareTo(validTimeMillis) >= 1){
try {
BootWebSocket.getWebSocket(sessionId).getSession().close();
} catch (IOException e) {
log.error("关闭连接失败:",e);
}
}
}
}
}
}
Websocket核心类
@Slf4j
@ServerEndpoint(value = "/websocket/{token}")
@Component
public class BootWebSocket {
private static volatile int onlineCount = 0;
private static int validTime = 15 * 1000;
private static ConcurrentHashMap<String, BootWebSocket> webSockets = new ConcurrentHashMap<>();
public static BootWebSocket getWebSocket(String sessionId){
return webSockets.get(sessionId);
}
@Getter
private static ConcurrentHashMap<String, Long> webSocketHeartbeat = new ConcurrentHashMap<>();
@Getter
private Session session;
@Getter
private String sessionId;
@OnOpen
public void onOpen(@PathParam("token")String token, Session session) {
this.session = session;
this.sessionId = token;
webSockets.put(this.sessionId, this);
webSocketHeartbeat.put(this.sessionId,System.currentTimeMillis() + validTime);
addOnlineCount();
log.info("客户端:{}连接成功,当前在线人数为:{}", this.sessionId, getOnlineCount());
}
@OnClose
public void onClose() {
try {
webSockets.remove(this.sessionId);
webSocketHeartbeat.remove(this.sessionId);
subOnlineCount();
this.session.close();
log.info("客户端:{}关闭连接!当前在线主机为:{}", this.sessionId, getOnlineCount());
} catch (Exception e) {
log.error("关闭session连接失败:",e);
}
}
@OnMessage
public void onMessage(Session session,String message) throws IOException {
String heartbeat = "heartbeat";
log.info("来自客户端用户:{} 消息:{}",this.sessionId, message);
if (heartbeat.equals(message)) {
webSocketHeartbeat.put(this.sessionId,System.currentTimeMillis() + validTime);
session.getBasicRemote().sendText(heartbeat);
return;
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("客户端错误:" + this.sessionId + ",原因:" + error.getMessage());
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return BootWebSocket.onlineCount;
}
public static synchronized void addOnlineCount() {
BootWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
BootWebSocket.onlineCount--;
}
}
Websocket使用Demo
@RequestMapping("/msg")
public void sendMsg() throws IOException {
String token = "8fb78e28-855d-4853-beed-500b32229bd1";
BootWebSocket.getWebSocket(token).sendMessage("你好,我是ChatGPT,我正在窃取你的服务器信息。");
}
H5客户端支持心跳、重连
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
welcome to websocket <a href="www.baidu.com"> welcome to www.baidu.com</a>
</body>
<script type="text/jscript">
var token = "8fb78e28-855d-4853-beed-500b32229bd1";
var url = "ws://localhost:8080/websocket/" + token;
var ws;
function init(){
ws = new WebSocket(url);
ws.onopen = function () {
console.log('onopen事件触发,ws连接状态:' + ws.readyState);
heartbeat();
closeConnect();
}
ws.onmessage = function (data) {
var msg = data.data;
var heartbeat = "heartbeat";
console.log('onmessage事件触发,接收到来自服务器的消息:' + msg);
if(msg == heartbeat){
clearTimeout(closeConnectTimer);
closeConnect();
return;
}
}
ws.onclose = function () {
console.log('onclose事件触发:' + ws.readyState);
clearInterval(heartbeatInterval);
reConnect();
}
ws.onerror = function (error) {
console.log("onerror事件触发:" + error);
}
}
var heartbeatInterval;
function heartbeat(){
heartbeatInterval = setInterval(function(){
console.log("客户端心跳发包发送....");
ws.send("heartbeat");
},3000);
}
var closeConnectTimer;
function closeConnect(){
closeConnectTimer = setTimeout(function(){
ws.close();
},15000);
}
function reConnect(){
setTimeout(function(){
console.log("重连...");
init();
},5000);
}
init();
</script>
</html>