zoukankan      html  css  js  c++  java
  • websocket心跳机制

    什么是websocket?

    WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

    它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

    其他特点包括:

    (1)建立在 TCP 协议之上,服务器端的实现比较容易。

    (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

    (3)数据格式比较轻量,性能开销小,通信高效。

    (4)可以发送文本,也可以发送二进制数据。

    (5)没有同源限制,客户端可以与任意服务器通信。

    (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

    一、何为心跳?

    心跳就是客户端定时的给服务端发送消息,证明客户端是在线的, 如果超过一定的时间没有发送则就是离线了。

    二、如何判断在线离线?

    当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,

    第二次客户端定时再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,

    得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;

    三、若服务端宕机了,客户端怎么做、服务端再次上线时怎么做?

    客户端则需要断开连接,通过onclose 关闭连接,服务端再次上线时则需要清除之间存的数据,若不清除 则会造成只要请求到服务端的都会被视为离线。

    四、使用springboot整合websocket

    添加Pom依赖

      

        <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
    
        websocket的配置类:
        @Configuration 
    public class WebSocketConfig {
        
        @Bean  
        public ServerEndpointExporter serverEndpointExporter() {  
            return new ServerEndpointExporter();  
        }  
    }
    
    websocket的API
    @ServerEndpoint("/websocket/{sid}")
    @Component
    @Slf4j
    public class WebSocketServer {
         
            //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
            private static int onlineCount = 0;
            //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
            private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
    
            //与某个客户端的连接会话,需要通过它来给客户端发送数据
            private Session session;
    
            //接收sid
            private String sid="";
            /**
             * 连接建立成功调用的方法*/
            @OnOpen
            public void onOpen(Session session,@PathParam("sid") String sid) {
                this.session = session;
                webSocketSet.add(this);     //加入set中
                addOnlineCount();           //在线数加1
                log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
                this.sid=sid;
                try {
                     sendMessage("连接成功");
                } catch (IOException e) {
                    log.error("websocket IO异常");
                }
            }
    
            /**
             * 连接关闭调用的方法
             */
            @OnClose
            public void onClose() {
                webSocketSet.remove(this);  //从set中删除
                subOnlineCount();           //在线数减1
                log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
            }
    
         
            @Autowired
            private RedisUtil<Object> redisUtil = (RedisUtil<Object>) SpringUtil.getBean("RedisUtil"); 
            
            /**
             * 收到客户端消息后调用的方法
             *
             * @param message 客户端发送过来的消息*/
            @OnMessage
            public void onMessage(String message, Session session) {
                //清空表
                
                log.info("收到来自窗口"+sid+"的信息:"+message);
                //clinet数据
                DeviceInfo Deviceinfo=FastJsonUtils.toBean(message, DeviceInfo.class);
                Deviceinfo.setBeatTime(System.currentTimeMillis());
                log.info(Deviceinfo.toString());
                String jsonStr=redisUtil.get(Deviceinfo.getDeviceId());
                if(null==jsonStr) {
                    redisUtil.set(Deviceinfo.getDeviceId(), Deviceinfo);
                }else {
                    //服务端数据
                    DeviceInfo redisData= FastJsonUtils.toBean(redisUtil.get(Deviceinfo.getDeviceId()),DeviceInfo.class);
                    
                    Long redistime= redisData.getBeatTime();
                    //当前系统时间
                    Long dates=   System.currentTimeMillis();
                    Long time=    dates-redistime;
                    log.info("endTime="+dates+"startTime="+redistime+"总时长="+time);
                    if(time<=15*1000) {
                        log.info(Deviceinfo.getDeviceId()+"在线");
                    }else {
                        log.info(Deviceinfo.getDeviceId()+"离线");
                    }
                     redisUtil.set(Deviceinfo.getDeviceId(), Deviceinfo);    
                }
               
               
                //群发消息
                for (WebSocketServer item : webSocketSet) {
                    try {
                        item.sendMessage(message);
                      //  item.sendInfo(message,sid);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            /**
             * 
             * @param session
             * @param error
             */
            @OnError
            public void onError(Session session, Throwable error) {
                log.error("发生错误");
                error.printStackTrace();
            }
            /**
             * 实现服务器主动推送
             */
            public void sendMessage(String message) throws IOException {
                this.session.getBasicRemote().sendText(message);
            }
    
    
            /**
             * 群发自定义消息
             * */
            public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
                log.info("推送消息到窗口"+sid+",推送内容:"+message);
                for (WebSocketServer item : webSocketSet) {
                    try {
                        //这里可以设定只推送给这个sid的,为null则全部推送
                        if(sid==null) {
                            item.sendMessage(message);
                        }else if(item.sid.equals(sid)){
                            item.sendMessage("server:"+message);
                        }
                    } catch (IOException e) {
                        continue;
                    }
                }
            }
    
            public static synchronized int getOnlineCount() {
                return onlineCount;
            }
    
            public static synchronized void addOnlineCount() {
                WebSocketServer.onlineCount++;
            }
    
            public static synchronized void subOnlineCount() {
                WebSocketServer.onlineCount--;
            }
    }        

    HTML页面
    <!DOCTYPE HTML>
    <html>
    <head>
        <title>Test My WebSocket</title>
        <meta charset="utf-8">
        <script type="text/javascript" src="../js/jquery.min.js"></script>
    </head>
     
    <body>
    TestWebSocket
    <input  id="texts" value="" type="text" />
    <button onclick="send()">SEND MESSAGE</button>
    <button οnclick="closeWebSocket()">CLOSE</button>
    <div id="message"></div>
     
    <script type="text/javascript">
    var lockReconnect = false;//避免重复连接
    var wsUrl = "ws://localhost:8889/websocket/001";
    var ws;
    var tt;
    function createWebSocket() {
      try {
        ws = new WebSocket(wsUrl);
        init();
      } catch(e) {
        console.log('catch');
        reconnect(wsUrl);
      }
    }
    function init() {
      ws.onclose = function () {
        console.log('链接关闭');
        reconnect(wsUrl);
      };
      ws.onerror = function() {
        console.log('发生异常了');
        reconnect(wsUrl);
      };
      ws.onopen = function () {
        //心跳检测重置
        heartCheck.start();
      };
      ws.onmessage = function (event) {
        //拿到任何消息都说明当前连接是正常的
        console.log('接收到消息');
        heartCheck.start();
      }
    }
    function reconnect(url) {
      if(lockReconnect) {
        return;
      };
      lockReconnect = true;
      //没连接上会一直重连,设置延迟避免请求过多
      tt && clearTimeout(tt);
      tt = setTimeout(function () {
        createWebSocket(url);
        lockReconnect = false;
      }, 4000);
    }
    //心跳检测
    var heartCheck = {
      timeout: 10000,
      timeoutObj: null,
      serverTimeoutObj: null,
      start: function(){
        console.log('start');
        var self = this;
        this.timeoutObj && clearTimeout(this.timeoutObj);
        this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
        this.timeoutObj = setTimeout(function(){
          //这里发送一个心跳,后端收到后,返回一个心跳消息,
       
          var timestamp = (new Date()).getTime();
          //,"beatTime":timestamp
          var obj={"deviceId":"001","userId":"119"};
          var a = JSON.stringify(obj);
          ws.send(a);
          self.serverTimeoutObj = setTimeout(function() {
           
            console.log(ws);
            ws.close();
           
          }, self.timeout);

        }, this.timeout)
      }
    }
    createWebSocket(wsUrl);
    </script>
    </body>

    </html>
  • 相关阅读:
    ASP.NET中Cookie编程的基础知识
    一道编程题
    软件开发一点心得
    迅雷产品经理笔试题
    常用JS 1
    设计模式
    整理思路
    抽象工厂模式 Abstract Factory
    单件模式(Single Pattern)
    序列化
  • 原文地址:https://www.cnblogs.com/hellohero55/p/12814831.html
Copyright © 2011-2022 走看看