Springboot WebSocket + Vue3 整合一篇通超详细图文

这篇具有很好参考价值的文章主要介绍了Springboot WebSocket + Vue3 整合一篇通超详细图文。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.前言

Springboot+vue3整合WebSocket(粗略简单能用)

2.后端代码

2.1.Maven

        <!--WebSocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.2.WebSocket配置类

2.2.1.WebSocket

package com.cn.websocket;

import jakarta.websocket.Session;

public class WebSocket {
    /**
     * session
     */
    private Session session;
    /**
     * 账号id
     */
    private String userId;

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}

2.2.2.WebSocketConfig

package com.cn.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
@EnableWebSocket
@Service
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

2.2.3.WebSocketUtil

package com.cn.websocket;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: tools
 * @Description: 通过这个类进行连接WebSocket的,默认发信息就进入onMessage解析
 */
@Component
@ServerEndpoint(value = "/test/webSocket/{userId}")
@Slf4j
public class WebSocketUtil {
	/**
	 * 登录连接数 应该也是线程安全的
	 */
	private static int loginCount = 0;
	/**
	 * user 线程安全的
	 */
	private static final Map<String, WebSocket> userMap = new ConcurrentHashMap<String, WebSocket>();

	/**
	 * @Description: 收到消息触发事件,这个消息是连接人发送的消息
	 * @Param [messageInfo, session]
	 * @Return: void
	 * {
	 * "userId": "test2",
	 * "message": "你收到了嘛?这是用户test发的消息!"
	 * }
	 **/
	@OnMessage
	public void onMessage(String messageInfo, Session session) throws IOException, InterruptedException {
		if (StringUtils.isBlank(messageInfo)) {
			return;
		}
		// 当前用户
		String userIdTo = session.getPathParameters().get("userId");
		// JSON数据
		log.info("onMessage:{}", messageInfo);
		Map map = JSON.parseObject(messageInfo, Map.class);
		// 接收人
		String userId = (String) map.get("userId");
		// 消息内容
		String message = (String) map.get("message");
		// 发送给指定用户
		sendMessageTo(message, userId);
		log.info(DateUtil.now() + " | " + userIdTo + " 私人消息-> " + message, userId);
	}

	/**
	 * @Description: 打开连接触发事件
	 * @Param [account, session, config]
	 * @Return: void
	 **/
	@OnOpen
	public void onOpen(@PathParam("userId") String userId, Session session, EndpointConfig config) {
		WebSocket webSocket = new WebSocket();
		webSocket.setUserId(userId);
		webSocket.setSession(session);
		boolean containsKey = userMap.containsKey(userId);
		if (!containsKey) {
			// 添加登录用户数量
			addLoginCount();
			userMap.put(userId, webSocket);
		}
		log.info("打开连接触发事件!已连接用户: " + userId);
		log.info("当前在线人数: " + loginCount);

	}

	/**
	 * @Description: 关闭连接触发事件
	 * @Param [session, closeReason]
	 * @Return: void
	 **/
	@OnClose
	public void onClose(@PathParam("userId") String userId, Session session, CloseReason closeReason) {
		boolean containsKey = userMap.containsKey(userId);
		if (containsKey) {
			// 删除map中用户
			userMap.remove(userId);
			// 减少断开连接的用户
			reduceLoginCount();
		}
		log.info("关闭连接触发事件!已断开用户: " + userId);
		log.info("当前在线人数: " + loginCount);

	}

	/**
	 * @Description: 传输消息错误触发事件
	 * @Param [error :错误]
	 * @Return: void
	 **/
	@OnError
	public void onError(Throwable error) {
		log.info("onError:{}", error.getMessage());
	}

	/**
	 * @Description: 发送指定用户信息
	 * @Param [message:信息, userId:用户]
	 * @Return: void
	 **/
	public void sendMessageTo(String message, String userId) throws IOException {
		for (WebSocket user : userMap.values()) {
			if (user.getUserId().equals(userId)) {
				user.getSession().getAsyncRemote().sendText(message);
			}
		}
	}

	/**
	 * @Description: 发给所有人
	 * @Param [message:信息]
	 * @Return: void
	 **/
	public void sendMessageAll(String message) throws IOException {
		for (WebSocket item : userMap.values()) {
			item.getSession().getAsyncRemote().sendText(message);
		}
	}

	/**
	 * @Description: 连接登录数增加
	 * @Param []
	 * @Return: void
	 **/
	public static synchronized void addLoginCount() {
		loginCount++;
	}

	/**
	 * @Description: 连接登录数减少
	 * @Param []
	 * @Return: void
	 **/
	public static synchronized void reduceLoginCount() {
		loginCount--;
	}

	/**
	 * @Description: 获取用户
	 * @Param []
	 * @Return: java.util.Map<java.lang.String, com.cn.webSocket.WebSocket>
	 **/
	public synchronized Map<String, WebSocket> getUsers() {
		return userMap;
	}

}

3.简易接口

package com.cn.controller.test.websocket;

import cn.hutool.core.date.DateUtil;
import com.cn.common.AjaxResult;
import com.cn.websocket.WebSocket;
import com.cn.websocket.WebSocketUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
 * @program: tools
 */
@Slf4j
@RestController
@RequestMapping("/test/webSocket")
public class WebSocketController {
    @Autowired
    private WebSocketUtil webSocketUtil;

    /**
     * 获取在线用户信息
     *
     * @return ok
     */
    @GetMapping(value = "/getUser")
    public AjaxResult getUser() {
        Map<String, WebSocket> users = webSocketUtil.getUsers();
        Set<String> ids = users.keySet();
        return AjaxResult.success(ids);
    }

    /**
     * 发送全体消息
     *
     * @param message 消息内容
     * @return ok
     */
    @GetMapping(value = "/sendMessageAll")
    public AjaxResult sendMessageAll(@RequestParam String message) {
        try {
            webSocketUtil.sendMessageAll(message);
            log.info(DateUtil.now() + " | " + "admin" + " 全体消息-> " + message);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("消息推送失败!");
        }
        return AjaxResult.success("消息推送成功:" + message);
    }

    /**
     * 发送消息给某人
     *
     * @return ok
     */
    @GetMapping(value = "/sendMessageTo")
    public AjaxResult sendMessageTo(@RequestParam String message, @RequestParam String userId) {
        try {
            webSocketUtil.sendMessageTo(message, userId);
            log.info(DateUtil.now() + " | " + "admin" + " 私人消息-> " + message, userId);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("消息推送失败!");
        }
        return AjaxResult.success();
    }
}

3.前端代码

结构:
vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

3.1.WebSocket.js(统一配置)

// 信息提示
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores'

// WebSocket地址
const url = 'ws://127.0.0.1:8001/test/webSocket/'

// WebSocket实例
let ws

// 重连定时器实例
let reconnectTimer

// WebSocket重连开关
let isReconnecting = false

// WebSocket对象
const websocket = {
  // WebSocket建立连接
  Init(username) {
    // 判断浏览器是否支持WebSocket
    if (!('WebSocket' in window)) {
      ElMessage.error('您的浏览器不支持 WebSocket')
      return
    }

    // 创建WebSocket实例
    ws = new WebSocket(url + username)

    // 监听WebSocket连接
    ws.onopen = () => {
      // ElMessage.success('WebSocket连接成功')
    }

    // 监听WebSocket连接错误信息
    ws.onerror = (e) => {
      console.log('WebSocket重连开关', isReconnecting)
      console.log('WebSocket数据传输发生错误', e)
      // ElMessage.error('WebSocket传输发生错误')
      // 打开重连
      reconnect()
    }

    // 监听WebSocket接收消息
    ws.onmessage = (e) => {
      console.log('WebSocket接收后端消息:' + e.data)
      // 心跳消息不做处理
      if (e.data === 'ok') {
        return
      }

      // 调用回调函数处理接收到的消息
      if (websocket.onMessageCallback) {
        websocket.onMessageCallback(e.data)
      }

      ElMessage.success(e.data)
    }
  },

  // WebSocket连接关闭方法
  Close() {
    // 关闭断开重连机制
    isReconnecting = true
    ws.close()
    // ElMessage.error('WebSocket断开连接')
  },

  // WebSocket发送信息方法
  Send(data) {
    // 处理发送数据JSON字符串
    const msg = JSON.stringify(data)
    // 发送消息给后端
    ws.send(msg)
  },

  // 暴露WebSocket实例,其他地方调用就调用这个
  getWebSocket() {
    return ws
  },

  // 新增回调函数用于处理收到的消息
  onMessageCallback: null,

  // 设置消息处理回调函数
  setMessageCallback(callback) {
    this.onMessageCallback = callback
  }
}

// 监听窗口关闭事件,当窗口关闭时-每一个页面关闭都会触发-扩张需求业务
window.onbeforeunload = function () {
  // 在窗口关闭时关闭 WebSocket 连接
  websocket.Close()
  console.log('WebSocket窗口关闭事件触发')
}

// 浏览器刷新重新连接
// 刷新页面后需要重连-并且是在登录之后
if (performance.getEntriesByType('navigation')[0].type === 'reload') {
  console.log('WebSocket浏览器刷新了')

  // 延迟一定时间再执行 WebSocket 初始化,确保页面完全加载后再进行连接
  setTimeout(() => {
    console.log('WebSocket执行刷新后重连...')
    // 刷新后重连
    // 获取username(假设为测试username写死,现在是动态获取)
    const username = useUserStore().user.username
    websocket.Init(username)
  }, 200) // 适当调整延迟时间
}

// 重连方法
function reconnect() {
  console.log('WebSocket重连开关', isReconnecting)
  // 判断是否主动关闭连接
  if (isReconnecting) {
    return
  }
  // 重连定时器-每次WebSocket错误方法onerror触发它都会触发
  reconnectTimer && clearTimeout(reconnectTimer)
  reconnectTimer = setTimeout(function () {
    console.log('WebSocket执行断线重连...')
    // 获取username(假设为测试username写死,现在是动态获取)
    const username = useUserStore().user.username
    websocket.Init(username)
    isReconnecting = false
  }, 4000)
}

// 暴露对象
export default websocket

3.2 WebSocket.vue

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { getUserService, sendMessageToService } from '../../api/websocket'
import websocket from '../../utils/websocket'

// 收到的消息
const receivedMessage = ref('')
// 输入框中的消息
const inputMessage = ref('')
// 输入框中的用户ID
const inputUserId = ref('')
// 在线用户
const userList = ref([])

const getMessageCallback = (message) => {
  receivedMessage.value = message
}

// 在组件挂载时设置消息处理回调
onMounted(() => {
  websocket.setMessageCallback(getMessageCallback)
})

// 在组件销毁前取消消息处理回调
onBeforeUnmount(() => {
  websocket.setMessageCallback(null)
})

// 获取在线用户
const getUserList = async () => {
  const res = await getUserService()
  userList.value = res.data.data
}

// 发送消息
const sendMessage = async () => {
  const userId = inputUserId.value
  const message = inputMessage.value

  // 调用发送消息的接口
  await sendMessageToService({ message, userId })
}
</script>

<template>
  <div>
    <!-- 输入框和按钮 -->
    <el-input v-model="inputUserId" placeholder="输入用户ID"></el-input>
    <el-input v-model="inputMessage" placeholder="输入消息"></el-input>
    <el-button type="" @click="sendMessage">发送消息</el-button>
  </div>
  <hr />
  <div>
    <div>收到的消息: {{ receivedMessage }}</div>
  </div>
  <hr />
  <div>
    <el-button type="primary" @click="getUserList">获取在线用户列表</el-button>
    <div class="userList">
      <ul>
        <li v-for="item in userList" :key="item.index">
          {{ item }}
        </li>
      </ul>
    </div>
  </div>
</template>

<style scoped>
.userList {
  background-color: pink;
}
</style>

3.3 WebSocket2.vue

<script setup>
import { ref } from 'vue'
import websocket from '../../utils/websocket'
import { getUserService, sendMessageToService } from '../../api/websocket'
import { useUserStore } from '@/stores'
// 用户信息
const userStore = useUserStore()
const username = userStore.user.username
// 收到的消息
const receivedMessage = ref('')

// 输入框中的消息
const inputMessage = ref('')
// 输入框中的用户ID
const inputUserId = ref('')

// 连接WebSocket
const connectWebSocket = async () => {
  await new Promise((resolve) => {
    websocket.Init(username)

    // 在 WebSocket 连接成功时调用 resolve
    websocket.getWebSocket().onopen = () => {
      console.log('WebSocket连接成功')
      resolve()
    }
  })

  // 使用 ref 包装 onmessage 回调
  websocket.getWebSocket().onmessage = (event) => {
    // 处理消息,这里你可以根据实际需求更新页面上的数据
    console.log('收到的消息WebSocket2:', event.data)
    // 更新收到的消息
    receivedMessage.value = event.data
  }
}

// 断开WebSocket连接
const disconnectWebSocket = () => {
  websocket.Close()
}

// 获取在线用户
const userList = ref([])
const getUserList = async () => {
  const res = await getUserService()
  userList.value = res.data.data
}

// 发送消息
const sendMessage = async () => {
  const userId = inputUserId.value // 使用输入框中的用户ID
  const message = inputMessage.value

  // 调用发送消息的接口
  await sendMessageToService({ message, userId })

  // 清空输入框
  // inputMessage.value = ''
}
</script>

<template>
  <div>
    <el-button type="primary" @click="connectWebSocket">连接WebSocket</el-button>
    <el-button type="danger" @click="disconnectWebSocket">断开WebSocket</el-button>
    <br />
    <!-- 输入框和按钮 -->
    <el-input v-model="inputUserId" placeholder="输入用户ID" style="width: 120px"></el-input>
    <el-input v-model="inputMessage" placeholder="输入消息"></el-input>
    <el-button type="" @click="sendMessage">发送消息</el-button>
  </div>
  <hr />
  <div>
    <span>收到的消息:{{ receivedMessage }}</span>
  </div>
  <hr />
  <div>
    <el-button type="primary" @click="getUserList">获取在线用户列表</el-button>
    <div class="userList">
      <ul>
        <li v-for="item in userList" :key="item.index">
          {{ item }}
        </li>
      </ul>
    </div>
    <div>
      <span>有缺点无法获取实时数据,必须断开在连接,应该是由于不是和登录一个作用域导致的</span>
    </div>
  </div>
</template>

<style scoped>
.userList {
  background-color: pink;
}
</style>

3.4.WebSocket.js

请求接口

import request from '@/utils/request'

// 获取用户信息
export const getUserService = () => request.get('/test/webSocket/getUser')

// 发送指定消息
export const sendMessageToService = ({ message, userId }) => {
  const params = {
    message,
    userId
  }
  return request.get('/test/webSocket/sendMessageTo', { params })
}

3.5.登录退出页

连接关闭
vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端
vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端
vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

4.页面展示

4.1.静态展示

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

4.2.发送消息弹框展示并展示在页面上

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端
vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

5.缺陷问题

切换到WebSocket2页面
断开两个用户的连接

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

在重新连接两个用户

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

5.1.问题1

重新发送信息(下图),接收了3次OK,但是没有弹框消息了!

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

5.2.问题2

刷新浏览器(下图),有了弹框但是页面消息没有!不知道为啥,估计是js作用域不一致导致的(难道是覆盖率用户名?)?

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

5.3.问题3

此时切换到WebSocket1页面,发现控制台接收到了消息,但是页面既没有弹框,也没有消息文字!

vue3集成websocket,Java驿站,前端驿站,spring boot,websocket,后端

欢迎各位大佬,如知原因麻烦告知,非常感谢!文章来源地址https://www.toymoban.com/news/detail-858184.html

到了这里,关于Springboot WebSocket + Vue3 整合一篇通超详细图文的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3 终端实现 (vue3+xterm+websocket)

      目录 一、xterm介绍 二、效果展示 三、vue文件实现代码 一、xterm介绍 xterm是一个使用 TypeScript 编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用,通常与 websocket 一起使用。 二、效果展示 三、vue文件实现代码

    2024年02月04日
    浏览(28)
  • vue3中使用websocket

    效果图 实现 src/util/socket.ts src/store/webSocket.ts 这里面放到了登录成功后在连接websocket src/viwes/login.vue 附赠后台建议websocket服务供测试使用 链接:https://pan.baidu.com/s/1RzbWiooLwCIuDTnEfN_x0Q?pwd=p58w 提取码:p58w

    2024年02月11日
    浏览(26)
  • 在vue3中封装使用WebSocket

    上篇文章记录了如何在日常开发过程中引入并使用websocket连接,但是在后续的开发过程中发现之前的写法有点问题,比如说多次引用连接会共用一个心跳,如果一个连接关掉了,后续其他的连接可能被一起关掉等等的bug。 所以在这篇文章里针对上篇文章提供的方法进行改进,

    2024年02月07日
    浏览(28)
  • vue3:websocket的封装与使用

    前言:vue3+pinia项目 1.引入ws 2.新建websocket.js类 3.新建一个pinia Store类 4.页面中使用

    2024年02月02日
    浏览(28)
  • 简单本地websocket运行实例(vue3)

    目录 1.完善一下登陆页面,方便后续使用 2.具体实现vue页面 3.hooks文件夹建立,存放websocket设置 4.建立src的同级server文件夹,用于后端启动websocket服务 5.创建index.js文件,编写websocket代码 6.在server目录下,通过npm run dev运行后端代码  7.效果展示 整体代码: 页面结构如图:  

    2024年02月06日
    浏览(27)
  • vue3使用websocket(亲测解决)

    1.需要后端给你一个ws的接口比如: 我这里的name是后端要求登录成功后搞得 2.后端给我个登录的接口,需要登录后才能实现长链接 3.封装init方法 网上找了一堆没用的方法,不建议看 所有代码合集

    2024年02月14日
    浏览(70)
  • 使用vue3简单实现WebSocket通信

    关于WebSocket通信的简单介绍: 握手阶段:在建立WebSocket连接之前,客户端需要发送一个HTTP请求到服务器,请求升级为WebSocket协议。这个过程称为握手(Handshake)。如果服务器支持WebSocket协议,它将返回带有特定标头的HTTP响应,表示握手成功。 建立连接:客户端收到服务器的

    2024年02月16日
    浏览(28)
  • vue3+ts+pinia整合websocket

    先有实时数据需要展示. 由于设备量极大且要对设备参数实时记录展示.axios空轮询不太适合. 选择websocket长连接通讯. 使用pinia原因是pinia具备共享数据性质.可以作为消息队列缓存数据,降低渲染压力.同时方便多个页面或组件获取websocket数据 安装pinia 注册pinia不再详细叙述,自行

    2024年02月11日
    浏览(27)
  • vue3.2+vite+代理,使用websocket

    之前以为websocket复杂,想使用插件来实现,查了一番资料,原生写法就很简单。 查询列表,需要实时获取员工上报的数据 Table.vue 因为跨域问题,这里使用了vite的proxy代理功能 vite.config.ts 如果代理设置好,连接成功,以上websocket会打印\\\"连接成功\\\"和输出返回。 完! 项目运行

    2024年02月10日
    浏览(37)
  • 基于Vue3封装一个好用的Websocket

    在Vue3中使用Websocket可以让我们轻松地实现实时数据传输。为了方便使用,我们可以封装一个好用的Websocket类。 首先我们需要安装 ws 库来处理Websocket连接,使用以下命令进行安装: 我们可以新建一个 websocket.js 文件,在其中定义一个 Websocket 类,代码如下: 以上代码中,我们

    2024年02月04日
    浏览(77)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包