springboot集成websocket+前端vue掉线重连机制

    xiaoxiao2023-10-23  127

    后端

    注入maven依赖

    <!--websocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>

    第一种方式

    websocket配置 package com.jx.fly.common.config; import com.jx.fly.module.mapper.LaborProjectConfigMapper; import com.jx.fly.websocket.WebSocketServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * websocket配置 * * @author zhongxiaojian * @date 2019-05-23 **/ @Configuration public class WebSocketConfig{ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } @Autowired public void setMapper(LaborProjectConfigMapper laborProjectConfigMapper){ WebSocketServer.laborProjectConfigMapper=laborProjectConfigMapper; } } websocket业务处理类 package com.jx.fly.websocket; import cn.hutool.log.StaticLog; import com.jx.fly.module.mapper.LaborProjectConfigMapper; import com.jx.smart.common.model.labor.attendance.project.LaborProjectConfigModel; 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.*; import java.util.concurrent.CopyOnWriteArraySet; /** * websocket处理类 * @author xiaojian * @date 2018/10/25 */ @ServerEndpoint("/websocket/{projectId}") @Component public class WebSocketServer { public static LaborProjectConfigMapper laborProjectConfigMapper; private static Map<Integer, CopyOnWriteArraySet<WebSocketServer>> SERVER_MAP = new HashMap<>(); private Session session; private int projectId; /** * 连接成功调用 * @param session * @param projectId */ @OnOpen public void onOpen(Session session,@PathParam("projectId") Integer projectId){ this.session = session; this.projectId = projectId; //业务处理 if (projectId != null){ LaborProjectConfigModel model = laborProjectConfigMapper.selectById(projectId); if (model != null){ CopyOnWriteArraySet<WebSocketServer> set = SERVER_MAP.get(projectId); if (set != null){ set.add(this); }else { CopyOnWriteArraySet<WebSocketServer> sessionSet = new CopyOnWriteArraySet<>(); sessionSet.add(this); SERVER_MAP.put(projectId,sessionSet); } StaticLog.info("session:"+session+"连接成功,"+model.getId()+"项目在线数量: "+SERVER_MAP.get(projectId).size()); sendMessage("连接成功"); }else { StaticLog.error("websocket IO异常"); sendMessage("非法连接"); } }else { sendMessage("projectId不能为空"); } } /** * 关闭连接调用 */ @OnClose public void onClose() { SERVER_MAP.forEach((k,v)->v.remove(this)); StaticLog.info("projectId:"+projectId+",session:"+session+"关闭连接,项目在线数量: "+SERVER_MAP.get(projectId).size()); } /** * 收到消息调用 * @param message * @param session */ @OnMessage public void onMessage(String message, Session session) { //群发消息 SERVER_MAP.forEach((k,v)->{ for (WebSocketServer item : v) { item.sendMessage(message); } }); } /** * 错误调用 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { StaticLog.error("session:"+session+"发生错误"); error.printStackTrace(); } /** * 推送 * @param message */ public void sendMessage(String message) { try { StaticLog.info("sendMessage projectId: "+projectId+",message: "+message); this.session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } /** * 发送消息 * @param message * @param projectId */ public static void send(int projectId,String message) { SERVER_MAP.forEach((k,v)->{ for (WebSocketServer item : v) { if(item.projectId == projectId ){ item.sendMessage(message); } } }); } }

    第二种方式

    websocket配置 package com.jx.fly.common.config; import com.jx.fly.module.mapper.LaborProjectConfigMapper; import com.jx.fly.websocket.WebSocketHandler; import com.jx.fly.websocket.WebSocketInterceptor; import com.jx.fly.websocket.WebSocketServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * websocket配置 * * @author zhongxiaojian * @date 2019-05-23 **/ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private LaborProjectConfigMapper laborProjectConfigMapper; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { webSocketHandlerRegistry.addHandler(new WebSocketHandler(), "/websocket/{projectId}").setAllowedOrigins("*").addInterceptors(new WebSocketInterceptor(laborProjectConfigMapper)); } } websocket业务处理类 package com.jx.fly.websocket; import cn.hutool.log.StaticLog; import com.alibaba.fastjson.JSONObject; import com.jx.fly.module.mapper.LaborProjectConfigMapper; import com.jx.smart.common.model.labor.attendance.project.LaborProjectConfigModel; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; /** * websocket处理类 * @author xiaojian * @date 2018/10/25 */ public class WebSocketHandler implements org.springframework.web.socket.WebSocketHandler { private static Map<Integer, CopyOnWriteArraySet<WebSocketSession>> SERVER_MAP = new HashMap<>(); private int projectId; /** * 连接成功调用 * @param session */ @Override public void afterConnectionEstablished(WebSocketSession session){ String url = session.getUri().toString(); projectId = Integer.parseInt(url.substring(url.lastIndexOf("/")+1)); //业务处理 CopyOnWriteArraySet<WebSocketSession> sessionSet = SERVER_MAP.get(projectId); if (sessionSet != null){ sessionSet.add(session); }else { sessionSet = new CopyOnWriteArraySet<>(); sessionSet.add(session); SERVER_MAP.put(projectId,sessionSet); } StaticLog.info(session+"连接成功,在线数量: "+SERVER_MAP.get(projectId).size()); } /** * 链接关闭调用 * @param session * @param status * @throws Exception */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception{ SERVER_MAP.forEach((k,v)->v.remove(session)); StaticLog.info(session+"关闭连接,在线数量: "+SERVER_MAP.get(projectId).size()); } /** * 是否WebSocketHandler处理部分消息 * @return */ @Override public boolean supportsPartialMessages() { return false; } /** * 接受消息 * @param webSocketSession * @param webSocketMessage * @throws Exception */ @Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception{ //群发消息 SERVER_MAP.forEach((k,v)->{ v.forEach(s->{ try { s.sendMessage(new TextMessage(webSocketMessage.getPayload().toString())); } catch (IOException e) { e.printStackTrace(); } }); }); } /** * 错误调用 * @param session */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) { StaticLog.error(session+",发生错误"); } /** * 发送消息 * @param message * @param projectId */ public static void send(int projectId,String message) { CopyOnWriteArraySet<WebSocketSession> set = SERVER_MAP.get(projectId); set.forEach(x->{ try { x.sendMessage(new TextMessage(message)); StaticLog.info("{} sendMessage: {}",projectId,message); } catch (IOException e) { e.printStackTrace(); } }); } } websocket拦截器 package com.jx.smart.fly.common.websocket; import cn.hutool.core.util.StrUtil; import cn.hutool.log.StaticLog; import com.jx.smart.fly.module.mapper.LaborProjectConfigMapper; import com.jx.smart.common.model.labor.attendance.project.LaborProjectConfigModel; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; import java.util.Map; /** * websoket拦截器 * * @author zhongxiaojian * @date 2019-05-25 **/ public class WebSocketInterceptor implements HandshakeInterceptor { private LaborProjectConfigMapper laborProjectConfigMapper; public WebSocketInterceptor(){ } public WebSocketInterceptor(LaborProjectConfigMapper laborProjectConfigMapper){ this.laborProjectConfigMapper = laborProjectConfigMapper; } /** * 握手之前 * @param serverHttpRequest * @param serverHttpResponse * @param webSocketHandler * @param map * @return * @throws Exception */ @Override public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception { if (serverHttpRequest instanceof ServletServerHttpRequest){ String url = serverHttpRequest.getURI().toString(); String projectId = url.substring(url.lastIndexOf("/")+1); if (StrUtil.isNotBlank(projectId)){ LaborProjectConfigModel model = laborProjectConfigMapper.selectById(Integer.parseInt(projectId)); if (model != null){ return true; }else { StaticLog.error("非法请求 : projectId = {}",projectId); } }else { StaticLog.error("projectId不能为空"); } } return false; } /** * 握手之后 * @param serverHttpRequest * @param serverHttpResponse * @param webSocketHandler * @param e */ @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { } }

    前端

    这里需要后端去配合,发送心跳,后端需要回心跳包

    <template> </template> <script> import audio from '../../assets/music/791865496.mp3'; export default { name: "Audio", data(){ return { ws:null, audio:null, playing:false, task1:null, task2:null, count:0 } }, methods:{ initWebSocket:function () { if(typeof(WebSocket) === "undefined"){ alert("您的浏览器不支持websocket") }else { this.ws = new WebSocket("ws:127.0.0.1:2021/websocket/ai"); this.ws.onopen = this.open; this.ws.onerror = this.error; this.ws.onmessage = this.getMessage; this.ws.onclose = this.close; } }, open: function () { console.log("[ws] 连接成功"); }, error: function () { console.log("[ws] 连接错误"); }, getMessage: function (msg) { let obj = JSON.parse(msg.data); if (obj === "heart") { window.clearInterval(this.task2); this.task2 = null; this.count = 0; console.log("[ws] 收到心跳"); }else { this.playAudio(); } }, send: function (message) { if(this.ws.readyState === WebSocket.OPEN){ this.ws.send(JSON.stringify(message)); } }, close: function () { console.log("[ws] 已经关闭"); }, heart:function () { console.log("[ws] 发送心跳"); this.count++; this.send("heart"); }, reconnection:function () { if (this.ws.readyState === WebSocket.CLOSED){ console.log("[ws] 正在尝试重连"); this.initWebSocket(); } }, initAudio:function(){ this.audio = new Audio(audio); this.audio.onended = this.onEnded; this.audio.onplay = this.onPlay; }, playAudio:function () { if(!this.playing){ this.audio.play(); } }, onPlay:function () { this.playing = true; }, onEnded:function () { this.playing = false; } }, watch:{ "count":function (e) { if (e >= 10 && this.task2 == null){ this.task2 = window.setInterval(this.reconnection, 3000); } } }, created: function () { this.initAudio(); this.initWebSocket(); this.task1 = window.setInterval(this.heart, 3000); }, destroyed :function () { this.ws.close(); window.clearInterval(this.task1); window.clearInterval(this.task2); } } </script> <style scoped> </style>
    最新回复(0)