WebSocket服务端数据推送及心跳机制(Spring Boot + VUE):

文章目录

        • 一、WebSocket简介:
        • 二、WebSocket通信原理及机制:
        • 三、WebSocket特点和优点:
        • 四、WebSocket心跳机制:
        • 五、在后端Spring Boot 和前端VUE中如何建立通信:
            • 【1】在Spring Boot 中 pom.xml中添加 websocket依赖
            • 【2】创建 WebSocketConfig.java 开启websocket支持
            • 【3】创建 WebSocketServer.java 链接
            • 【4】创建一个测试调用websocket发送消息 TimerSocketMessage.java (用定时器发送推送消息)
            • 【5】在VUE中创建和后端 websocket服务的连接并建立心跳机制
            • 【6】启动项目开始测试结果


一、WebSocket简介:

HTML5规范在传统的web交互基础上为我们带来了众多的新特性,随着web技术被广泛用于web APP的开发,这些新特性得以推广和使用,而websocket作为一种新的web通信技术具有巨大意义。WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说,服务器可以在任意时刻发送消息给浏览器。支持双向通信。

二、WebSocket通信原理及机制:

websocket是基于浏览器端的web技术,那么它的通信肯定少不了http,websocket本身虽然也是一种新的应用层协议,但是它也不能够脱离http而单独存在。具体来讲,我们在客户端构建一个websocket实例,并且为它绑定一个需要连接到的服务器地址,当客户端连接服务端的时候,会向服务端发送一个消息报文

三、WebSocket特点和优点:
  1. 支持双向通信,实时性更强。
  2. 更好的二进制支持。
  3. 较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
  4. 支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
  5. 建立在tcp协议之上,服务端实现比较容易
  6. 数据格式比较轻量,性能开销小,通信效率高
  7. 和http协议有着良好的兼容性,默认端口是80和443,并且握手阶段采用HTTP协议,因此握手的时候不容易屏蔽,能通过各种的HTTP代理
四、WebSocket心跳机制:
  1. 在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,这时候websocket的连接已经断开,而浏览器不会执行websocket 的 onclose方法,我们无法知道是否断开连接,也就无法进行重连操作。如果当前发送websocket数据到后端,一旦请求超时,onclose便会执行,这时候便可进行绑定好的重连操作。
  2. 心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连~
五、在后端Spring Boot 和前端VUE中如何建立通信:
【1】在Spring Boot 中 pom.xml中添加 websocket依赖
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
【2】创建 WebSocketConfig.java 开启websocket支持
package com.example.demo.websocket;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
 
/**
 * 开启WebSocket支持
 * @author zh
 */
@Configuration
public class WebSocketConfig {
 
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
【3】创建 WebSocketServer.java 链接
package com.example.demo.websocket;
 
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
 
 
@ServerEndpoint("/websocket/testsocket")
@Component
public class WebSocketServer {
 
    private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
 
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
 
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新窗口开始监听,当前在线人数为" + getOnlineCount());
        try {
            sendMessage("连接成功");
        } catch (Exception e) {
            log.error("websocket IO异常");
        }
    }
 
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        try {
            webSocketSet.remove(this);  //从set中删除
            subOnlineCount();           //在线数减1
            session.close();
            log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到的信息:"+message);
        Map<String, Object> maps = new HashMap<>();
        maps.put("type", message);
        this.sendInfo(maps);
    }
 
    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(Object obj)  {
        try {
            synchronized (this.session) {
                this.session.getBasicRemote().sendText((JSON.toJSONString(obj)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
 
    /**
     * 群发自定义消息
     * */
    public static void sendInfo(Object obj) {
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(obj);
            } catch (Exception e) {
                continue;
            }
        }
    }
 
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
 
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
 
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}
【4】创建一个测试调用websocket发送消息 TimerSocketMessage.java (用定时器发送推送消息)
package com.example.demo.websocket;
 
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import java.util.HashMap;
import java.util.Map;
 
 
@Component
@EnableScheduling
public class TimerSocketMessage {
 
    /**
     * 推送消息到前台
     */
    @Scheduled(cron = "*/1 * * * * * ")
    public void SocketMessage(){
        Map<String, Object> maps = new HashMap<>();
        maps.put("type", "sendMessage");
        maps.put("data","11111");
        WebSocketServer.sendInfo(maps);
    }
}
【5】在VUE中创建和后端 websocket服务的连接并建立心跳机制
<template>
  <div class="hello">
    <h1> websocket 消息推送测试:{{data}}</h1>
  </div>
</template>

<script>
  export default {
    name: 'HelloWorld',
    data () {
      return {
        data:0,
        timeout: 28 * 1000,//30秒一次心跳
        timeoutObj: null,//心跳心跳倒计时
        serverTimeoutObj: null,//心跳倒计时
        timeoutnum: null,//断开 重连倒计时
        websocket: null,
      }
    },
    created () {
      // 初始化websocket
      this.initWebSocket()
    },
    methods:{
      initWebSocket () {
        let url = 'ws://localhost:8086/websocket/testsocket'
        this.websocket = new WebSocket(url)
        // 连接错误
        this.websocket.onerror = this.setErrorMessage

        // 连接成功
        this.websocket.onopen = this.setOnopenMessage

        // 收到消息的回调
        this.websocket.onmessage = this.setOnmessageMessage

        // 连接关闭的回调
        this.websocket.onclose = this.setOncloseMessage

        // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = this.onbeforeunload
      },
      reconnect () { // 重新连接
        if(this.lockReconnect) return;
        this.lockReconnect = true;
        //没连接上会一直重连,设置延迟避免请求过多
        this.timeoutnum && clearTimeout(this.timeoutnum);
        this.timeoutnum = setTimeout(() => {
          //新连接
          this.initWebSocket();
          this.lockReconnect = false;
        }, 5000);
      },
      reset () { // 重置心跳
        // 清除时间
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        // 重启心跳
        this.start();
      },
      start () { // 开启心跳
        this.timeoutObj && clearTimeout(this.timeoutObj);
        this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
        this.timeoutObj = setTimeout(() => {
          // 这里发送一个心跳,后端收到后,返回一个心跳消息,
          if (this.websocket && this.websocket.readyState == 1) { // 如果连接正常
            this.websocketsend('heartbeat');
          } else { // 否则重连
            this.reconnect();
          }
          this.serverTimeoutObj = setTimeout(() => {
            //超时关闭
            this.websocket.close();
          }, this.timeout);

        }, this.timeout)
      },
      setOnmessageMessage (event) {
        let obj = JSON.parse(event.data);
        console.log("obj",obj)
        switch(obj.type) {
          case 'heartbeat':
            //收到服务器信息,心跳重置
            this.reset();
            break;
          case 'sendMessage':
            this.data = obj.data
            console.log("接收到的服务器消息:",obj.data)
        }

      },
      setErrorMessage () {
        //重连
        this.reconnect();
        console.log("WebSocket连接发生错误" + '   状态码:' + this.websocket.readyState)
      },
      setOnopenMessage () {
        //开启心跳
        this.start();
        console.log("WebSocket连接成功" + '   状态码:' + this.websocket.readyState)
      },
      setOncloseMessage () {
        //重连
        this.reconnect();
        console.log( "WebSocket连接关闭" + '   状态码:' + this.websocket.readyState)
      },
      onbeforeunload () {
        this.closeWebSocket();
      },
      //websocket发送消息
      websocketsend(messsage) {
        this.websocket.send(messsage)
      },
      closeWebSocket() { // 关闭websocket
        this.websocket.close()
          },
  }
}
</script>
 
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
【6】启动项目开始测试结果


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/348122.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python + Selenium —— 元素定位函数 find_element!

WebDriver 中的 find_element() 方法用来查找元素&#xff0c;并返回 WebElement 对象。是 WebDriver 中最常用的方法。 前面提到的八种定位方式都有对应的方法&#xff0c;如find_element_by_id()。 在 WebDriver 中还有一种用法&#xff0c;就是单纯的find_element()。需要…

年销180万辆的特斯拉,护城河却在崩塌

文&#xff5c;刘俊宏 2023年率先开启汽车价格战的马斯克&#xff0c;伤敌一百自损八千&#xff1f; 在1月25日的特斯拉2023Q4财报电话会上&#xff0c;特斯拉CEO马斯克对中国公司的竞争力如此感叹道&#xff0c;“要是没有贸易壁垒&#xff0c;他们将摧毁&#xff08;destroy…

FastDeploy项目简介,使用其进行(图像分类、目标检测、语义分割、文本检测|orc部署)

FastDeploy是一款全场景、易用灵活、极致高效的AI推理部署工具&#xff0c; 支持云边端部署。提供超过 &#x1f525;160 Text&#xff0c;Vision&#xff0c; Speech和跨模态模型&#x1f4e6;开箱即用的部署体验&#xff0c;并实现&#x1f51a;端到端的推理性能优化。包括 物…

Redis配置类,序列化

解释说明&#xff1a; 当前配置类不是必须的&#xff0c;因为 Spring Boot 框架会自动装配 RedisTemplate 对象&#xff0c;但是默认的key序列化器为JdkSerializationRedisSerializer&#xff0c;导致我们存到Redis中后的数据和原始数据有差别 如&#xff1a; 但是取是可以取出…

Linux第35步_在“移植uboot”前安装“libncurses5-dev,bison和flex”工具

在“移植uboot”前&#xff0c;需要在Ubuntu中安装“libncurses5-dev&#xff0c;bison和flex”工具&#xff0c;否则在“编译uboot”时&#xff0c;会报错。 一、了解相关知识 1、libncurses5-dev库是一个在Linux/Unix下广泛应用的图形函数库。 2、bison是用C编写的语法解析…

web蓝桥杯真题--13、水果摆盘

背景介绍 目前 CSS3 中新增的 Flex 弹性布局已经成为前端页面布局的首选方式&#xff0c;这次试题将利用 Flex 实现经典布局效果。 准备步骤 在开始答题前&#xff0c;你需要在线上环境终端中键入以下命令&#xff0c;下载并解压所提供的文件。 wget https://labfile.oss.a…

Vue+OpenLayers7入门到实战:快速搭建Vue+OpenLayers7地图脚手架项目。从零开始构建Vue项目并整合OpenLayers7.5.2

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 本章针对Vue初学者,对Vue不熟悉,甚至还不会Vue的入门学生读者。 本章会详细讲解从NodeJS环境到npm环境的各个步骤,再到使用vue-cli脚手架快速生成项目,以及添加OpenLayers7地图库依赖,编写简单的xyz高德地图显示…

AR 自回归模型

文章目录 总的代码ADF 检验(是否平稳)差分操作拟合AR 模型预测可视化总的代码 import pandas as pd import numpy as np import matplotlib.pyplot as plt from statsmodels.tsa.ar_model import AutoReg from statsmodels.tsa.stattools import adfuller# 生成一个示例时间序…

DualSPHysics源码结构解读,新手入门

DualSPHysics代码下载&#xff0c;进入官网&#xff1a;https://dual.sphysics.org/ 可以看到下载的地方有①Full package ②Source code&#xff0c;官方的解读是&#xff1a;如果你只是想运行案例的话就下载Full package&#xff0c;如果想要自己进行修改构建的话&#xff0…

【数据结构】72变的双端队列

双端队列 前言一、双端队列1.1 双端队列的定义1.2 输入受限的双端队列1.3 输出受限的双端队列1.5 输入输出都受限的双端队列1.6 小结 二、双端队列的使用2.1 双端队列的出队序列——暴力求解2.1.1 栈的出栈序列2.1.2 输入受限的双端队列2.1.3 输出受限的双端队列2.1.4 输入输出…

Qt基础-项目中添加文件夹及子文件

本文讲解Qt项目中添加文件夹 1、项目中新建文件夹 打开工程目录&#xff0c;在目录下建立文件夹&#xff0c;如建立文件Dialogs 2、移入文件 将需要归类的头文件.h和源文件.cpp放入该文件夹下&#xff1b; 3、4、项目-右键-添加现有文件 查看Pro文件 发生了改变 4、也可以采…

Spring 声明式事务讲解,和 @Transactional注解的用法

目录 一、Spring框架介绍 二、什么是声明式事务 三、如何解决并发性事务问题 四、Transactional注解的用法 一、Spring框架介绍 Spring框架是一个开源的Java应用程序开发框架&#xff0c;旨在简化企业级Java应用程序的开发。它提供了一种轻量级的、全面的编程和配置模型&a…

【第六课课后作业】大模型评测

大模型评测 大模型评测安装环境安装数据准备查看支持的数据集和模型 启动测评评测结果 大模型评测 安装 环境安装 conda create --name opencompass --clone/root/share/conda_envs/internlm-base source activate opencompass git clone https://github.com/open-compass/ope…

一、Lamdba 表达式与函数式接口(最终版)

一、Lamdba 表达式与函数式接口 1.1 Lamdba 表达式与函数式接口 1.1.1 Lambda 表达式概述 Lambda 表达式是 Java 8 引入的一个新特性Lambda 表达式可以被视为匿名函数允许在需要函数的地方以更简洁的方法定义功能Lambda 表达式可以完成简洁的函数定义Stream API 中大量使用了…

基于静态顺序表实现通讯录

目录 一、设计框架 1、功能要求​ 2、菜单函数的实现 二、头文件实现​ Contact.h SeqList.h 三、Test.h 四、通讯录的初始化和销毁 五、增加通讯录 六、在通讯录中查找姓名下标 七、删除通讯录 八、显示通讯录 九、查找通讯录 一、设计框架 test.c&#xff1a;通…

【学网攻】 第(5)节 -- Cisco VTP的使用

文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口【学网攻】 第(4)节 -- 交换机划分Vlan 前言 网络已经成为了我们生活中不可或缺的一部分&#xff0c;它连接了世界各地的人们&#xff0c;让信息和资…

重磅!证监会回应股市波动!2万亿元救市计划正在商榷!将提振比特币?

最近这段时间&#xff0c;国内资本市场震荡走弱、波动加大&#xff0c;一些投资者深感忧虑。多家机构表示&#xff0c;市场波动已引起高层的重视。 继1月23日&#xff0c;证监会党委扩大会议从宏观层面提出资本市场建设发力重点后&#xff0c;1月24日证监会副主席王建军的一席采…

【前端基础--4】

定位属性 position 可以将元素定位到你想要放到位置&#xff0c;使用方位值来进行移动(top,left,right,bottom)。 1.相对定位 position: relative; top: 20px; left: 20px; 以自身为定点进行移动&#xff0c;不会脱离文档流。 不会影响元素本身的性质&#xff1b;块级…

YOLOv5改进系列(28)——添加DSConv注意力卷积(ICCV 2023|用于管状结构分割的动态蛇形卷积)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

【OpenCV学习笔记22】- OpenCV中的轮廓 - 开始了解轮廓

这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子&#xff0c;也会给出自己根据官方的例子完成的更改代码&#xff0c;同样彩蛋的实现也会结合多个知识点一起实现一些小功能&#xff0c;来帮助我们对学会的知识点进行结合应用。 如果有喜欢我笔…
最新文章