首页 > 其他分享 >前端和后台建立websocket长连接,并添加心跳检测

前端和后台建立websocket长连接,并添加心跳检测

时间:2022-12-04 11:44:58浏览次数:55  
标签:websocket log clientId 心跳 session 后台 连接 客户端

Nginx代理服务器默认1分钟会导致websocket长连接端口,所以服务端与客户端需要添加心跳检测

1.前端代码Vue

  data () {
    return {
      showDeviceDialog: true,
      banner: [],
      deviceId: '',
      timeout: 55000, // 55秒发一次心跳,比nginx代理端默认连接超时时间(1分钟)稍微小一点,在接近断开的情况下以通信的方式去重置连接时间
      serverTimeoutObj: null,
      websocket_connected_count: 0, //已尝试连接次数
    }
  },
  watch: {
    deviceId (newVal, OldVal) {
      if (newVal) {
        const routerUrl = this.$router.resolve({
          name: 'detail',
          params: { id: newVal }
        })
        window.open(routerUrl.href, '_blank')
      }
    }
  },
  components: {
    selectDevice
  },
  methods: {
    seeMore () {
      const routerUrl = this.$router.resolve({
        name: 'list'
      })
      window.open(routerUrl.href, '_blank')
      // this.$router.push({ name: 'list' })
    },
    confirmSelectDevice (active) {
      this.showDeviceDialog = false
      this.createWebsocket(active)
    },
    createWebsocket(active) {
      let port;
      switch(active) {
        case 1:
            port = 8092;
            break;
        case 2:
            port = 8093;
            break;
        case 3:
            port = 8094;
            break;
      }
      const clientId = port + '@' + new Date().getTime();
      const httpURL = process.env.NODE_ENV === 'production' ? 'wss://henyouliao.cn' : 'ws://192.168.2.135:8005';
      this.websocket = new WebSocket(`${httpURL}/websocket/${clientId}`);
      // 连接发生错误的回调方法,连接错误时会继续尝试发起连接(尝试5次)
      this.websocket.onerror = () => {
        console.log("websocket发生了错误" + (this.websocket_connected_count > 0 ? ",已重连" + this.websocket_connected_count + "次" : ""));
        if( ++this.websocket_connected_count <= 5) {
          this.createWebsocket(active);
        }
      };
      // 连接成功建立的回调方法
      this.websocket.onopen = () => {
        console.log("websocket已打开(" + new Date().format('yyyy-MM-dd hh:mm:ss') + ")");
        //成功建立连接后,重置心跳检测
        this.heartCheckReset();
        this.heartCheckStart(active);
      };
      // 接收到消息的回调方法
      this.websocket.onmessage = (msg) => {
        //如果获取到消息,说明连接是正常的,重置心跳检测
        this.heartCheckReset();
        this.heartCheckStart(active);
        //拿到后端返回的心跳pong说明连接正常
        if(msg.data && String(msg.data).toLowerCase().trim() !== "pong") {
          console.log('websocket接收到消息', msg.data);
          this.deviceId = msg.data
        }
      };
      // 连接关闭的回调方法
      this.websocket.onclose = () => {
        console.log("websocket已关闭(" + new Date().format('yyyy-MM-dd hh:mm:ss') + ")");
      };
      // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
      this.websocket.onbeforeunload = () => {
        console.log('监听到窗口关闭事件,主动关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常');
        this.websocket.close();
        this.heartCheckReset();
      };
    },
    // 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间,如果已经断开了,发起重连。
    heartCheckReset() {
      clearTimeout(this.serverTimeoutObj);
    },
    heartCheckStart(active) {
      this.serverTimeoutObj = setTimeout(() =>{
        if(this.websocket.readyState == 1){
          console.log("websocket连接状态,发送心跳检测ping维持心跳连接...");
          this.websocket.send("ping");
          //如果获取到消息,说明连接是正常的,重置心跳检测
          this.heartCheckReset();
          this.heartCheckStart(active);
        } else {
          console.log("websocket断开状态,尝试重连");
          this.createWebsocket(active);
        }
      }, this.timeout)
    }
}

2.后台代码SpringBoot

/**
 * @author Kisen
 * @email [email protected]
 * @date 2022/11/16 11:28
 * @detail websocket server端
 * clientId格式: 机器socket端口@时间戳
 * 8092,8093,8094端口分别对应1号机、2号机、3号机
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{clientId}")
public class WebSocketServer {

    //用来存放每个客户端对应的WebSocket对象
    public static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //当前连接客户端的用户唯一标识
    private String clientId;

    /**
     * 连接建立成功调用的方法
     *
     * @param session
     * @param clientId
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("clientId") String clientId) {
        this.session = session;
        this.clientId = clientId;
        // clientId是用来表示唯一客户端,如果需要指定发送,需要指定发送通过clientId来区分
        webSocketMap.put(clientId, this);
        log.info("[WebSocket]客户端{}连接成功,客户端标识:{},当前在线连接数为:{}", session.getId(), clientId, webSocketMap.size());
    }

    /**
     * 连接关闭调用的方法
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        webSocketMap.remove(this.clientId);
        log.info("[WebSocket]客户端{}连接断开,客户端标识:{},当前在线连接数为:{}", session.getId(), clientId, webSocketMap.size());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到客户端{} 的消息:{}", session.getId(), message);
        //心跳检测响应
        if (StringUtils.equalsIgnoreCase("ping", message)) {
            sendMessage("pong");
            log.info("[WebSocket]服务端 已回复客户端{} 的心跳检测: pong", session.getId());
        }
    }

    @OnError
    public void one rror(Session session, Throwable error) {
        log.error("客户端{} 发生错误:{}", session.getId(), error);
    }

    /**
     * 实现服务器主动推送
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("客户端{} 发生消息({})失败:{}", session.getId(), message, e);
            throw new RuntimeException(e);
        }
    }

}

标签:websocket,log,clientId,心跳,session,后台,连接,客户端
From: https://www.cnblogs.com/yydscn/p/16949577.html

相关文章

  • simpread-WebSocket 教程
    WebSocket是一种网络通信协议,很多高级功能都需要它。WebSocket是一种网络通信协议,很多高级功能都需要它。本文介绍WebSocket协议的使用方法。一、为什么需要Web......
  • 电脑技巧:如何用快捷键打开指定后台进程(或未在后台进程的) windows
    首先要知道,windows自带你按win+数字可以打开任务栏的程序,   想打开浏览器用win+1你可能有这样的顾虑,这是固定的吗?就算我重新启动了呢。那你只需右击“固定......
  • Ubutu Mosquitto部署和相关的配置(支持websocket)
    最近公司使用MQTT对公司现有的物联网设备进行重组,有幸能研究关于MQTT的一个服务端Mosquitto,虽然支持Socket的方式很好部署,但是在WebSoket的部署上还是走了很多弯路,网上查询......
  • nginx反向代理websocket,ws转wss
    记录一下nginx规则location^~/echargenet-admin#本地代理目录{proxy_passhttp://127.0.0.1:8080/xxx;#websocket地址proxy_set_headerHost$host;#......
  • webSocket基本使用
    //创建ws实例,建立连接(ws://121.40.165.18.8800有广告)    varws=newWebSocket('wss://javascript.info/article/websocket/demo/hello')    //......
  • nohup--将程序放入后台执行
    作用:可以将程序以忽略挂起信号的方式运行,常与&一起使用语法: nohupCommand[Arg…][&] 将程序放到后台运行的方法: command& 后台运行,关掉终端后程序停止......
  • springboot 加入websocket后,ServerEndpointExporter配置不识别-解决
    1.背景springboot加入websocket,需要配置ServerEndpointExporter的bean,发现没法识别2.原因springboot内置了tomcat,内置的tomcat与websocket不兼容,因此需要将-start-w......
  • Mono后台进程制作
    要解决的问题解决C#开发的控制台在Mono下面后台运行的问题,由于我遇到的问题比较特殊,需要用到第三方的回调,但是在回调过程中出现问题:使用anyexec,发现后台服务可以启动,但是Lo......
  • Mono后台服务设计
    解决的问题mono-service无法满足要求,启动过程老是出问题解决方案1.使用shell脚本完成后台进程的启动工作!.sh文件内容mono/usr/myfoder/camtest/Debug/DREQPAPP.exe>/d......
  • ajax发送post后台无法获取问题
    作为一名后端小渣渣一名。最近项目中出现了由于get请求携带参数过长而导致无法发出请求的问题,由于才疏学浅只能想到用post请求来解决此问题。get参数长度受限制,post不受......