vue前端实现
<script>
import store from '@/store/'
export default {
name: "HeaderNotice",
data () {
return {
websock: null,
heartCheck:null,
isNormalClose;false,
}
},
computed:{
},
mounted() {
this.initWebSocket();
this.heartCheckFun();
},
destroyed: function () { // 离开页面生命周期函数
this.websocketClose();
},
methods: {
initWebSocket: function () {
// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
var userId = store.getters.userInfo.id;
var url = window._CONFIG['domianURL'].replace("https://","wss://").replace("http://","ws://")+"/websocket/"+userId;
this.websock = new WebSocket(url);
this.websock.onopen = this.websocketOnopen;
this.websock.onerror = this.websocketOnerror;
this.websock.onmessage = this.websocketOnmessage;
this.websock.onclose = this.websocketOnclose;
},
websocketOnopen: function () {
console.log("WebSocket连接成功");
//心跳检测重置
this.heartCheck.reset().start();
},
websocketOnerror: function (e) {
console.log("WebSocket连接发生错误");
this.reconnect();
},
websocketOnmessage: function (e) {
console.log("-----接收消息-------",e.data);
var data = eval("(" + e.data + ")"); //解析对象
if(data.cmd == "topic"){
//系统通知
/.. 系统通知的处理逻辑../
}else if(data.cmd == "user"){
//用户消息
/.. 用户消息的处理逻辑../
}
//心跳检测重置
this.heartCheck.reset().start();
},
websocketOnclose: function (e) {
console.log("connection closed (" + e + ")");
if(e){
console.log("connection closed (" + e.code + ")");
}
if(isNormalClose===false){
this.reconnect();
}
},
websocketSend(text) { // 数据发送
try {
this.websock.send(text);
} catch (err) {
console.log("send failed (" + err.code + ")");
}
},
reconnect() {
var that = this;
//没连接上会一直重连,设置延迟避免请求过多
setTimeout(function () {
console.info("尝试重连...");
that.initWebSocket();
}, 5000);
},
websocketClose: function (e) {
this.isNormalClose=true
//会触发onclose回调方法
this.websock.close();
},
heartCheckFun(){
var that = this;
//心跳检测,每20s心跳一次
that.heartCheck = {
timeout: 20000,
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
//clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
that.websocketSend("HeartBeat");
console.info("客户端发送心跳");
//self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
// that.websock.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
//}, self.timeout)
}, this.timeout)
}
}
},
}
}
</script>
后端java实现
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.jeecg.common.constant.WebsocketConst;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}") //此注解相当于设置访问URL
public class WebSocket {
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="userId")String userId) {
try {
this.session = session;
webSockets.add(this);
sessionPool.put(userId, session);
log.info("【websocket消息】有新的连接,总数为:"+webSockets.size());
} catch (Exception e) {
}
}
@OnClose
public void onClose() {
try {
webSockets.remove(this);
log.info("【websocket消息】连接断开,总数为:"+webSockets.size());
} catch (Exception e) {
}
}
@OnMessage
public void onMessage(String message) {
log.debug("【websocket消息】收到客户端消息:"+message);
JSONObject obj = new JSONObject();
obj.put(WebsocketConst.MSG_CMD, WebsocketConst.CMD_CHECK);//业务类型
obj.put(WebsocketConst.MSG_TXT, "心跳响应");//消息内容
session.getAsyncRemote().sendText(obj.toJSONString());
}
//后端主动关闭连接(会在前端触发onclose回调方法)
public void closeWebSocket(String userId) {
try {
Session session = sessionPool.get(userId);
session.close();
} catch (Exception e) {
}
}
// 此为广播消息
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();
}
}
}
}
}
在shiro或其他安全框架中放开对/websocket/{userId}的拦截
无论前端还是后端主动关闭websocket都会触发前端的关闭回调方法websock.onclose