springboot集成websocket实现心跳维持,防止自动断开

1.maven依赖

                

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

 

2.前端代码

<!DOCTYPE HTML>
<html>
<head>
    <title>大屏A</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" /><button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
    var websocket = null;
    var wsGroupId = "20191009170924631538723061895168";
    var type = "screenA"
    var url = "ws://localhost:8080/ws/" + wsGroupId + "/" + type;
    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket(url);
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        // setMessageInnerHTML("error");
         console.log(getNowTime() +' 发生异常了');
         reconnect(url);
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        //setMessageInnerHTML("open");
        console.log(getNowTime() +" Socket 已打开");  
        send("heartbeat");
        //心跳检测重置
        heartCheck.start()
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        //websocket.setMessageInnerHTML(event.data);
        // 维持心跳
        heartCheck.start();
        if(event.data !='heartbeat'){
            document.getElementById("text").value=event.data;
            console.log(event.data)
        }
        //window.location.href="http://www.jb51.net";
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
       // websocket.setMessageInnerHTML("close");
        console.log(getNowTime() +" Socket已关闭");  
        reconnect(url)
    }

//    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
//     window.onbeforeunload = function(){
//         websocket.close();
//     }

    //将消息显示在网页上
 //    function setMessageInnerHTML(innerHTML){
 //        document.getElementById('message').innerHTML += innerHTML + '<br/>';
 //    }

    //关闭连接
 //    function closeWebSocket(){
 //        websocket.close();
//         lockReconnect= false;
//         reconnect(url);
//     }

    //发送消息
    function send(message){
       // var message = document.getElementById('text').value;
        websocket.send(message);
    }
    
    var lockReconnect = false;//避免重复连接
    //重试连接socket
    function reconnect(url) {
        if(lockReconnect) {
            return;
        };
        lockReconnect = true;
        //没连接上会一直重连,设置延迟避免请求过多
        tt && clearTimeout(tt);
        tt = setTimeout(function () {
            createWebSocket(url);
            lockReconnect = false;
        }, 10000);
    }
    //心跳检测
    var heartCheck = {
        timeout: 5000,
        timeoutObj: null,
        serverTimeoutObj: null,
        start: function(){
              console.log(getNowTime() +" Socket 心跳检测");  
            var self = this;
            this.timeoutObj && clearTimeout(this.timeoutObj);
            this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
            this.timeoutObj = setTimeout(function(){
                //这里发送一个心跳,后端收到后,返回一个心跳消息,
                //onmessage拿到返回的心跳就说明连接正常
                console.log(getNowTime() +' Socket 连接重试');
                send("heartbeat");
                self.serverTimeoutObj = setTimeout(function() {
                    console.log(this);
                    close();
                }, self.timeout);
            }, this.timeout)
        }
    }

        /**
         * 获取系统当前时间
         * @returns
         */
        function p(s) {
            return s < 10 ? '0' + s : s;
         }
        function getNowTime() {
            var myDate = new Date();
            //获取当前年
            var year = myDate.getFullYear();
            //获取当前月
            var month = myDate.getMonth() + 1;
            //获取当前日
            var date = myDate.getDate();
            var h = myDate.getHours();       //获取当前小时数(0-23)
            var m = myDate.getMinutes();     //获取当前分钟数(0-59)
            var s = myDate.getSeconds();
            return year + '-' + p(month) + "-" + p(date) + " " + p(h) + ':' + p(m) + ":" + p(s);
        }

</script>
</html>

 

 

3.后台代码

      

/**
 * @author feiyang
 */
@ServerEndpoint(value = "/ws/{wsGroupId}/{type}")
@Component
public class WebSocket {

    private static final Logger logger = LoggerFactory.getLogger(WebSocket.class);

    private static SessionStorage sessionStorage = SessionStorage.getInstance();

    private static Map<String, String> sessionGroup = new ConcurrentHashMap<>(16);

    /**
     * 连接建立成功调用的方法
     * @param session
     * @param type
     * @param wsGroupId
     * @throws Exception
     */
    @OnOpen
    public void onOpen(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type, Session session) {
        sessionGroup.put(session.getId(), wsGroupId);
        sessionStorage.putSession(session, wsGroupId, type);
        session.setMaxIdleTimeout(3600000);
        logger.info("websocket连接成功 sessionId:" + session.getId());
        logger.info("websocket连接成功 type:" + type);
        logger.info("websocket连接成功 wsGroupId:" + wsGroupId);
        logger.info("websocket链接数量:" + sessionGroup.size());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type, Session session) {
        logger.info("websocket连接关闭成功 sessionId:" + session.getId());
        logger.info(type);
        logger.info(sessionGroup.get(session.getId()));
        SessionStorage.getInstance().removeSession(sessionGroup.get(session.getId()), type);
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type,String message, Session session) {
        try {
            logger.info(message);
            sessionStorage.sendTextSingle( wsGroupId,  type,  message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("websocket连接失败 sessionId:" + session.getId());
    }
}

 

 

package com.example.springtest01.socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;

import javax.websocket.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * 作为对外的工具类
 * @author zhoukx
 */
public class SessionStorage {
    private Map<String, Map<String, Session>> sessionStore = new ConcurrentHashMap<>();

    private static volatile SessionStorage storage;

    private static final Logger logger = LoggerFactory.getLogger(SessionStorage.class);

    private static final String  FLAG_HEART_BEAT = "heartbeat";

    private SessionStorage() {
    }

    /**
     *   双端检索机制创建一个   SessionStorage 实例
     * @return
     */
    public static synchronized SessionStorage getInstance() {
        if(storage == null) {
            synchronized(SessionStorage.class) {
                if(storage == null) {
                    storage = new SessionStorage();
                }
            }
        }
        return storage;
    }

    /**
     * 将ws连接会话分组保存
     * @author  feiyang
     * @param session
     * @param wsGroupId
     * @param type
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    public void putSession(Session session, String wsGroupId, String type) {
        if(session != null) {
            Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
            if (sessionGroup == null) {
               sessionGroup = new ConcurrentHashMap<>(16);
               this.sessionStore.put(wsGroupId, sessionGroup);
            }
            sessionGroup.put(type, session);
        }
    }

    /**
     * ws连接关闭时删除会话
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    public void removeSession(String wsGroupId, String type) {
        Map<String, Session> sessionGroup = sessionStore.get(wsGroupId);
        sessionGroup.remove(type);
        if (sessionGroup.isEmpty()) {
            this.sessionStore.remove(wsGroupId);
        }
    }

    /**
     * 获取指定ws连接会话
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @return  javax.websocket.Session
     * @date    2019/9/19
     * @throws
     */
    public Session getSession(String wsGroupId, String type) {
        Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
        return sessionGroup.get(type);
    }

    /**
     * 获取指定ws连接会话组全部session
     * @author  feiyang
     * @param wsGroupId
     * @return  java.util.List<javax.websocket.Session>
     * @date    2019/9/19
     * @throws
     */
    public List<Session> getSessions(String wsGroupId) {
        Session[] sessions = new Session[]{};
        Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
        return Arrays.asList(sessionGroup.values().toArray(sessions));
    }

    /**
     * 获取全部的会话
     * @author  feiyang
     * @param
     * @return  java.util.List<javax.websocket.Session>
     * @date    2019/9/19
     * @throws
     */
    public List<Session> getAllSession() {
        List<Session> sessionList = new ArrayList<>();
        for (Map<String, Session> sessionGroup : sessionStore.values()) {
            Session[] sessions = new Session[]{};
            sessionList.addAll(Arrays.asList(sessionGroup.values().toArray(sessions)));
        }
        return sessionList;
    }

    /**
     * 针对单个会话发送文本消息
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @param text
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    @Async
    public void sendTextSingle(String wsGroupId, String type, String text){
        try {
            Session session = getSession(wsGroupId, type);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(text);
                if (text.equals(FLAG_HEART_BEAT)) {
                    logger.info("socket维持链接状态:" + text);
                } else {
                    logger.info("已发送》》》》》" + text);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 针对会话组发送文本信息
     * @author  feiyang
     * @param wsGroupId
     * @param text
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    @Async
    public void  sendTextGroup(String wsGroupId, String text) {
        try {
            List<Session> sessions = getSessions(wsGroupId);
                for(Session session : sessions) {
                    if (session.isOpen()) {
                        session.getBasicRemote().sendText(text);
                        logger.info("已发送》》》》》" + text);
                    }
                }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//    /**
//     * 针对全部会话发送消息
//     * @author  feiyang
//     * @param text
//     * @return  void
//     * @date    2019/9/19
//     * @throws
//     */
//    public void sendTextAll(String text) {
//        try {
//            List<Session> sessions = getAllSession();
//            for(Session session : sessions) {
//                if (session.isOpen()) {
//                    session.getBasicRemote().sendText(text);
//                    logger.info("已发送》》》》》" + text);
//                }
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
}

 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter (){
        return new ServerEndpointExporter();
    }
}
实现Spring Boot集成WebSocket实现消息推送,需要进行以下步骤: 1. 添加Spring Boot WebSocket依赖 2. 创建WebSocket配置类 3. 创建WebSocket处理器类 4. 创建WebSocket拦截器类 5. 创建WebSocket消息模型类 6. 在Spring Boot中使用WebSocket 下面是一个简单的示例代码: 1. 添加Spring Boot WebSocket依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建WebSocket配置类 创建一个WebSocket配置类,用于配置WebSocket相关的参数,如下所示: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/websocket").addInterceptors(new WebSocketInterceptor()); } } ``` 3. 创建WebSocket处理器类 创建一个WebSocket处理器类,用于处理WebSocket连接、断开连接和接收消息等操作,如下所示: ``` public class WebSocketHandler extends TextWebSocketHandler { private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(message); } } } ``` 4. 创建WebSocket拦截器类 创建一个WebSocket拦截器类,用于拦截WebSocket连接请求,如下所示: ``` public class WebSocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } } ``` 5. 创建WebSocket消息模型类 创建一个WebSocket消息模型类,用于封装WebSocket消息,如下所示: ``` public class WebSocketMessage { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ``` 6. 在Spring Boot中使用WebSocket 在Spring Boot中使用WebSocket非常简单,只需要在Controller中注入WebSocketSession即可,如下所示: ``` @Controller public class WebSocketController { @Autowired private WebSocketSession session; @MessageMapping("/send") public void send(WebSocketMessage message) throws Exception { session.sendMessage(new TextMessage(message.getContent())); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值