0


websocket 实现后端主动前端推送数据、及时通讯(vue3 + springboot)

简介

WebSocket 是一种全双工通信协议,用于在 Web 浏览器和服务器之间建立持久的连接。

  1. WebSocket 协议由 IETF 定为标准,WebSocket API 由 W3C 定为标准。
  2. 一旦 Web 客户端与服务器建立连接,之后的全部数据通信都通过这个连接进行。
  3. 可以互相发送 JSON、XML、HTML 或图片等任意格式的数据。

WebSocket 与 HTTP 协议的异同:

相同点:

  • 都是基于 TCP 的应用层协议。
  • 都使用 Request/Response 模型进行连接的建立。
  • 可以在网络中传输数据。

不同点:

  • WebSocket 使用 HTTP 来建立连接,但定义了一系列新的 header 域,这些域在 HTTP 中并不会使用。
  • WebSocket 支持持久连接,而 HTTP 协议不支持持久连接。

WebSocket 优点:
高效性: 允许在一条 WebSocket 连接上同时并发多个请求,避免了传统 HTTP 请求的多个 TCP 连接。
WebSocket 的长连接特性提高了效率,避免了 TCP 慢启动和连接握手的开销。
节省带宽:HTTP 协议的头部较大,且请求中的大部分头部内容是重复的。WebSocket 复用长连接,避免了这一问题。
服务器推送:WebSocket 支持服务器主动推送消息,实现实时消息通知。

WebSocket 缺点:
长期维护成本:服务器需要维护长连接,成本较高。
浏览器兼容性:不同浏览器对 WebSocket 的支持程度不一致。
受网络限制:WebSocket 是长连接,受网络限制较大,需要处理好重连。

WebSocket 应用场景:

  • 实时通信领域:
  • 社交聊天弹幕
  • 多玩家游戏
  • 协同编辑
  • 股票基金实时报价
  • 体育实况更新
  • 视频会议/聊天
  • 基于位置的应用
  • 在线教育
  • 智能家居等需要高实时性的场景。

一、后端代码

1、安装核心jar包: spring-boot-starter-websocket
<dependencies><!-- SpringBoot Websocket --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.1.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.22</version></dependency></dependencies>
2. 来一个配置类注入
packagecom.codeSE.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.socket.server.standard.ServerEndpointExporter;@ConfigurationpublicclassWebSocketConfig2{@BeanpublicServerEndpointExporterserverEndpointExporter(){returnnewServerEndpointExporter();}}
3. 写个基础webSocket服务
importcn.hutool.log.Log;importcn.hutool.log.LogFactory;importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.JSONObject;//import org.apache.commons.lang3.StringUtils;importorg.springframework.stereotype.Component;importorg.springframework.util.StringUtils;importjavax.websocket.*;importjavax.websocket.server.PathParam;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.concurrent.ConcurrentHashMap;/**
 * @ClassName: 开启WebSocket支持
 */@ServerEndpoint("/dev-api/websocket/{userId}")@ComponentpublicclassWebSocketServer{staticLog log =LogFactory.get(WebSocketServer.class);/**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
     */privatestaticint onlineCount =0;/**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */privatestaticConcurrentHashMap<String,WebSocketServer> webSocketMap =newConcurrentHashMap<>();/**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */privateSession session;/**
     * 接收userId
     */privateString userId ="";/**
     * 连接建立成功调用的方法
     */@OnOpenpublicvoidonOpen(Session session,@PathParam("userId")String userId){this.session = session;this.userId = userId;if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            webSocketMap.put(userId,this);//加入set中}else{
            webSocketMap.put(userId,this);//加入set中addOnlineCount();//在线数加1}

        log.info("用户连接:"+ userId +",当前在线人数为:"+getOnlineCount());try{sendMessage("连接成功");}catch(IOException e){
            log.error("用户:"+ userId +",网络异常!!!!!!");}}/**
     * 连接关闭调用的方法
     */@OnClosepublicvoidonClose(){if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);//从set中删除subOnlineCount();}
        log.info("用户退出:"+ userId +",当前在线人数为:"+getOnlineCount());}/**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */@OnMessagepublicvoidonMessage(String message,Session session){
        log.info("用户消息:"+ userId +",报文:"+ message);//可以群发消息//消息保存到数据库、redisif(!StringUtils.isEmpty(message)){try{//解析发送的报文JSONObject jsonObject =JSON.parseObject(message);}catch(Exception e){
                e.printStackTrace();}}}/**
     * @param session
     * @param error
     */@OnErrorpublicvoidonError(Session session,Throwable error){
        log.error("用户错误:"+this.userId +",原因:"+ error.getMessage());
        error.printStackTrace();}/**
     * 实现服务器主动推送
     */publicvoidsendMessage(String message)throwsIOException{this.session.getBasicRemote().sendText(message);}/**
     * 实现服务器主动推送
     */publicstaticvoidsendAllMessage(String message)throwsIOException{ConcurrentHashMap.KeySetView<String,WebSocketServer> userIds = webSocketMap.keySet();for(String userId : userIds){WebSocketServer webSocketServer = webSocketMap.get(userId);
            webSocketServer.session.getBasicRemote().sendText(message);System.out.println("webSocket实现服务器主动推送成功userIds===="+ userIds);}}/**
     * 发送自定义消息
     */publicstaticvoidsendInfo(String message,@PathParam("userId")String userId)throwsIOException{
        log.info("发送消息到:"+ userId +",报文:"+ message);if(!StringUtils.isEmpty(message)&& webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);}else{
            log.error("用户"+ userId +",不在线!");}}publicstaticsynchronizedintgetOnlineCount(){return onlineCount;}publicstaticsynchronizedvoidaddOnlineCount(){WebSocketServer.onlineCount++;}publicstaticsynchronizedvoidsubOnlineCount(){WebSocketServer.onlineCount--;}}
4. 写一个测试类,定时向客户端推送数据或者可以发起请求推送
packagecom.codeSE.controller;importcom.alibaba.fastjson2.JSONObject;importcom.codeSE.config.WebSocketServer;importorg.springframework.scheduling.annotation.Scheduled;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;importjava.util.HashMap;importjava.util.Map;@RestController@RequestMapping("/money")publicclassTest{//设置定时十秒一次@Scheduled(cron ="0/10 * * * * ?")@PostMapping("/send")publicStringsendMessage()throwsException{Map<String,Object> map =newHashMap<>();// 获取当前日期和时间LocalDateTime nowDateTime =LocalDateTime.now();DateTimeFormatter dateTimeFormatter =DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");System.out.println(dateTimeFormatter.format(nowDateTime));
        map.put("server_time",dateTimeFormatter.format(nowDateTime));
        map.put("server_code","200");
        map.put("server_message","这是服务器推送到客户端的消息哦!!");JSONObject jsonObject =newJSONObject(map);WebSocketServer.sendAllMessage(jsonObject.toString());return jsonObject.toString();}}
5. 启动springboot
packagecom.codeSE;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.web.servlet.ServletComponentScan;importorg.springframework.scheduling.annotation.EnableScheduling;@EnableScheduling//定时任务@ServletComponentScan//webSocket@SpringBootApplicationpublicclassWebSocketAppMain{publicstaticvoidmain(String[] args){SpringApplication.run(WebSocketAppMain.class);}}

使用网上的测试工具试一下:http://coolaf.com/tool/chattest 或者http://www.jsons.cn/websocket/
效果如下:
在这里插入图片描述
在这里插入图片描述

二、前端代码

使用vue3和原生websocket

1、简单写一个websocket的公共类

需求:commentUtil/WebsocketTool.js

//需求:在JavaScript中实现WebSocket连接失败后3分钟内尝试重连3次的功能,你可以设置一个重连策略,//     包括重连的间隔时间、尝试次数以及总时间限制。/**
 * @param {string} url  Url to connect
 * @param {number} maxReconnectAttempts Maximum number of times
 * @param {number} reconnect Timeout
 * @param {number} reconnectTimeout Timeout
 *
 */classWebSocketReconnect{constructor(url, maxReconnectAttempts =3, reconnectInterval =20000, maxReconnectTime =180000){this.url = url
    this.maxReconnectAttempts = maxReconnectAttempts
    this.reconnectInterval = reconnectInterval
    this.maxReconnectTime = maxReconnectTime
    this.reconnectCount =0this.reconnectTimeout =nullthis.startTime =nullthis.socket =nullthis.connect()}//连接操作connect(){
    console.log('connecting...')this.socket =newWebSocket(this.url)//连接成功建立的回调方法this.socket.onopen=()=>{
      console.log('WebSocket Connection Opened!')this.clearReconnectTimeout()this.reconnectCount =0}//连接关闭的回调方法this.socket.onclose=(event)=>{
      console.log('WebSocket Connection Closed:', event)this.handleClose()}//连接发生错误的回调方法this.socket.onerror=(error)=>{
      console.error('WebSocket Connection Error:', error)this.handleClose()//重连}}//断线重连操作handleClose(){if(this.reconnectCount <this.maxReconnectAttempts &&(this.startTime ===null|| 
    Date.now()-this.startTime <this.maxReconnectTime)){this.reconnectCount++
      console.log(`正在尝试重连 (${this.reconnectCount}/${this.maxReconnectAttempts})次...`)this.reconnectTimeout =setTimeout(()=>{this.connect()},this.reconnectInterval)if(this.startTime ===null){this.startTime = Date.now()}}else{
      console.log('超过最大重连次数或重连时间超时,已放弃连接!Max reconnect attempts reached or exceeded max reconnect time. Giving up.')this.reconnectCount =0// 重置连接次数0this.startTime =null// 重置开始时间}}//清除重连定时器clearReconnectTimeout(){if(this.reconnectTimeout){clearTimeout(this.reconnectTimeout)this.reconnectTimeout =null}}//关闭连接close(){if(this.socket &&this.socket.readyState === WebSocket.OPEN){this.socket.close()}this.clearReconnectTimeout()this.reconnectCount =0this.startTime =null}}// WebSocketReconnect 类封装了WebSocket的连接、重连逻辑。// maxReconnectAttempts 是最大重连尝试次数。// reconnectInterval 是每次重连尝试之间的间隔时间。// maxReconnectTime 是总的重连时间限制,超过这个时间后不再尝试重连。// reconnectCount 用于记录已经尝试的重连次数。// startTime 用于记录开始重连的时间。// connect 方法用于建立WebSocket连接,并设置相应的事件监听器。// handleClose 方法在WebSocket连接关闭或发生错误时被调用,根据条件决定是否尝试重连。// clearReconnectTimeout 方法用于清除之前设置的重连定时器。// close 方法用于关闭WebSocket连接,并清除重连相关的状态。// 使用示例// const webSocketReconnect = new WebSocketReconnect('ws://your-websocket-url')// 当不再需要WebSocket连接时,可以调用close方法// webSocketReconnect.close();exportdefault WebSocketReconnect
2、在任意Vue页面
<template><div><el-inputv-model="textarea1":rows="5"type="textarea"placeholder="请输入"/></div></template><scriptsetup>import{ ref, reactive,, onMounted, onUnmounted }from'vue'import WebSocketReconnect from'@/commentUtil/WebsocketTool'// --------------------------------------------let textarea1 =ref('【消息】---->')let websocket =null//判断当前浏览器是否支持WebSocketif('WebSocket'in window){//连接WebSocket节点
  websocket =newWebSocketReconnect('ws://127.0.0.1:8080'+'/dev-api/websocket/1122334455')}else{alert('浏览器不支持webSocket')}//接收到消息的回调方法
websocket.socket.onmessage=function(event){let data = event.data
  console.log('后端传递的数据:'+ data)//将后端传递的数据渲染至页面
  textarea1.value = textarea1.value + data +'\n'+'【消息】---->'}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload=function(){
  websocket.close()}//关闭连接functioncloseWebSocket(){
  websocket.close()}//发送消息functionsend(){
  websocket.socket.send({kk:123})}//------------------------------------</script><stylescoped></style>

效果:
在这里插入图片描述


本文转载自: https://blog.csdn.net/m0_48853274/article/details/136536626
版权归原作者 梦境之冢 所有, 如有侵权,请联系我们删除。

“websocket 实现后端主动前端推送数据、及时通讯(vue3 + springboot)”的评论:

还没有评论