思路
在我之前的一篇文章当中写到了websocket的实时刷新,但是有个问题没有解决,就是长时间没有数据的时候,这个连接就会自动断开,然后再次进行连接的话,需要再次进行连接。如果加入心跳机制的话,10秒钟客户端向服务端发送数据,服务端接收到数据,发条消息,告诉客户端我还在,客户端接收到消息,知道我们还是连接的状态,就没有必要再进行连接。如果客户端发送的消息,服务端没有对这个消息进行响应,则说明已经断开连接,则需要重新连接。
服务端代码如下:
package cn.lsmsp.alarm.snmp.config;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocket/{shopId}")
//此注解相当于设置访问URL
public class WebSocket {
private Logger logger = LoggerFactory.getLogger(WebSocket.class);
private Session session;
private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
private static Map<String, Session> sessionPool = new HashMap<String, Session>();
@OnOpen
public void onOpen(Session session, @PathParam(value = "shopId") String shopId) throws InterruptedException {
this.session = session;
webSockets.add(this);
// sessionPool.put(shopId, session);
// System.out.println("【websocket消息】有新的连接,总数为:"+webSockets.size());
System.out.println("【websocket消息】有新的连接啦!!!"+LocalDateTime.now());
// webSocketXdxService.xdxTest(shopId);
}
@OnClose
public void onClose(Session session) {
try {
webSockets.remove(this);
session.close();
System.out.println("【websocket消息】连接断开,总数为:" + webSockets.size());
} catch (Exception e) {
e.printStackTrace();
}
}
@OnMessage
public void onMessage(String message) {
logger.info("【websocket消息】收到客户端消息:" + message);
Map<String, Object> maps = new HashMap<>();
maps.put("type", message);
this.sendAllMessage(JSON.toJSONString(maps));
}
// 此为广播消息
public void sendAllMessage(String message) {
for (WebSocket webSocket : webSockets) {
System.out.println("【websocket消息】广播消息:" + message);
try {
webSocket.session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息 (发送文本)
public void sendTextMessage(String shopId, String message) {
Session session = sessionPool.get(shopId);
if (session != null) {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息 (发送对象)
public void sendObjMessage(String shopId, Object message) {
Session session = sessionPool.get(shopId);
if (session != null) {
try {
session.getAsyncRemote().sendObject(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
相对于上次,结合我这边的业务进行了调整,删除了session的存储,就变成了一个用户多个聊天窗口的那种概念。
客户端代码如下:
<template>
<div>
<h1>测试webSocket</h1>
<div id ="aaa" style="height: 300px; overflow-y: scroll; background: #333; color: #aaa; padding: 10px;"></div>
</div>
</template>
<script>
var that;
export default {
data(){
return {
data:0,
timeout: 30 * 1000,//30秒一次心跳
timeoutObj: null,//心跳心跳倒计时
serverTimeoutObj: null,//心跳倒计时
timeoutnum: null,//断开 重连倒计时
websocket: null,
}
},
created() { // 页面创建生命周期函数
that = this;
that.initWebSocket()
},
destroyed: function () { // 离开页面生命周期函数
that.websocketclose();
},
methods: {
initWebSocket: function () {
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
that.websocket = new WebSocket("wss://127.0.0.1:9231/kh_snmptrap/websocket/lsmsp");
that.websocket.onopen = that.websocketonopen;
that.websocket.onerror = that.websocketonerror;
that.websocket.onmessage = that.setOnmessageMessage;
that.websocket.onclose = that.websocketclose;
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
// window.onbeforeunload = that.onbeforeunload
},
reconnect: function () { // 重新连接
if(that.lockReconnect) return;
that.lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
that.timeoutnum && clearTimeout(that.timeoutnum);
that.timeoutnum = setTimeout(() => {
//新连接
that.initWebSocket();
that.lockReconnect = false;
}, 5000);
},
reset: function () { // 重置心跳
// 清除时间
clearTimeout(that.timeoutObj);
clearTimeout(that.serverTimeoutObj);
// 重启心跳
that.start();
},
start: function () { // 开启心跳
that.timeoutObj && clearTimeout(that.timeoutObj);
that.serverTimeoutObj && clearTimeout(that.serverTimeoutObj);
that.timeoutObj = setTimeout(() => {
// 这里发送一个心跳,后端收到后,返回一个心跳消息,
if (that.websocket && that.websocket.readyState == 1) { // 如果连接正常
that.websocketsend('heartbeat');
} else { // 否则重连
that.reconnect();
}
that.serverTimeoutObj = setTimeout(() => {
//超时关闭
that.websocket.close();
}, that.timeout);
}, that.timeout)
},
setOnmessageMessage: function(event) {
let obj = JSON.parse(event.data);
// console.log("obj",obj)
switch(obj.type) {
case 'heartbeat':
//收到服务器信息,心跳重置
that.reset();
break;
case 'sendMessage':
that.data = obj.data
console.log("接收到的服务器消息:",JSON.stringify(obj.data,null,4))
document.getElementById('aaa').innerHTML=JSON.stringify(obj.data,null,4)
}
},
websocketonopen: function () {
//开启心跳
that.start();
console.log("WebSocket连接成功!!!"+new Date()+"----"+that.websocket.readyState);
},
websocketonerror: function (e) {
console.log("WebSocket连接发生错误" + e);
},
websocketonmessage: function (e) {
console.log(e.data); // console.log(e);
document.getElementById('aaa').innerHTML=e.data
},
websocketclose: function (e) {
that.websocket.close();
console.log("connection closed ");
},
websocketsend(messsage) {
that.websocket.send(messsage)
},
closeWebSocket() { // 关闭websocket
that.websocket.close()
}
}
}
</script>
<style >
</style>
相对于删上次,加入了心跳的时间。这样的话客户端会收到两种类型的数据,heartbeat和sendMessage,便于区别哪个是心跳数据,哪个是发送的数据。还有一点,我这里是https的请求,如果你是http请求的话,把wss://127.0.0.1:9231改为ws://127.0.0.1:9231。其它想知道的,参考我之前的一篇文章:springboot websocket 实时刷新(亲测可用版)