vue+websocket实现即时聊天平台

目录

1 什么是websocket

2 实现步骤

2.1 导入依赖

2.2 编写代码


1 什么是websocket

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它主要用于在客户端和服务器之间建立持久的连接,允许实时数据交换。WebSocket 的设计目的是为了提高 Web 应用程序的交互性,减少延迟和带宽的使用。

  • 全双工通信:客户端和服务器可以同时发送和接收数据,而不需要等待对方完成发送。

  • 持久连接:建立一次连接后,可以保持该连接,直到主动关闭。这比传统的 HTTP 请求/响应模型更加高效。

  • 低延迟:由于不需要为每个请求建立新的连接,WebSocket 可以显著减少延迟。

  • 节省带宽:在 WebSocket 中,只有数据被发送而不需要携带大量的头部信息,这减少了带宽的消耗。

2 实现步骤

实施前提:默认在springBoot环境下实施

2.1 导入依赖

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

2.2 编写代码

WebSocketConfig:主要实现websocket的一些配置
package com.hyh.admin.config.websocket;import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;import javax.servlet.ServletContext;
import javax.servlet.ServletException;/*** WebSocket配置* @author hyh*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements ServletContextInitializer {/**  ServerEndpointExporter 作用*  这个Bean会自动注册使用@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}/** 解除websocket对数据大小的限制* @param servletContext Servlet上下文**/@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {// 解除websocket对数据大小的限制servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","10240000");servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","10240000");}
}
WebSocketSingleServe:具体的实现聊天的实时代码需求
package com.hyh.admin.config.websocket;import com.hyh.ad.common.core.domain.model.SysUser;
import com.hyh.admin.config.websocket.context.SpringBeanContext;
import com.hyh.admin.domain.Messages;
import com.hyh.admin.service.MessageService;
import com.hyh.admin.sys.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** WebSocket 单聊服务端*/
@ServerEndpoint("/singleChat/{username}")
@Component
public class WebSocketSingleServe implements InitializingBean {private static final Logger log = LoggerFactory.getLogger(WebSocketSingleServe.class);// 记录当前在线的连接public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(@PathParam("username") String username, Session session) {// 将用户的session放入map中session.getUserProperties().put("username", username);sessionMap.put(username, session);log.info("用户:{}",session.getUserProperties().get("username"));log.info("用户:{} 连接成功,session:{},总数:{}", username, session.getId(), sessionMap.size());}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(Session session) {try {sessionMap.values().remove(session);log.info("连接关闭,session:{},总数:{}", session.getId(), sessionMap.size());} catch (Exception e) {log.error("连接关闭异常:{}", e.getMessage());}}/*** 收到客户端消息后调用的方法* @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session fromSession) {// 假设消息格式为 "username:file:data"String[] parts = message.split(":", 3);if (parts.length == 3) {String targetUsername = parts[0].trim(); // 目标用户String type = parts[1].trim(); // 消息类型(text/file)String content = parts[2].trim(); // 消息内容log.info("收到消息:{},类型:{},内容:{}", targetUsername, type, content);// 根据类型处理消息if ("text".equals(type)) {// 发送文本消息sendMessageToUser(targetUsername, content, type);} else if ("image".equals(type)) {// 发送文件消息sendFileToUser(targetUsername, content, type);}else if ("file".equals(type)) {// 发送文件消息sendFileToUser(targetUsername, content, "file");}// 消息持久化String username = (String) fromSession.getUserProperties().get("username");saveMessage(username, targetUsername, content, type);}}/** 消息持久化*/private void saveMessage(String sendUsername, String targetUsername, String msg, String type) {// 保存消息try {MessageService messageService = SpringBeanContext.getContext().getBean(MessageService.class);ISysUserService sysUserService = SpringBeanContext.getContext().getBean(ISysUserService.class);SysUser targetUser = sysUserService.selectUserByUserName(targetUsername);Long targetUserId = targetUser.getId();SysUser sendUser = sysUserService.selectUserByUserName(sendUsername);Long userId = sendUser.getId();Messages messages = new Messages();messages.setSenderId(userId);messages.setReceiverId(targetUserId);messages.setContent(msg);messages.setMessageType(type); // 保存消息类型messageService.addMessage(messages);log.info("消息持久化成功");} catch (Exception e) {log.error("消息持久化失败:{}", e.getMessage());}}/**  发送文件给用户*/private void sendFileToUser(String targetUsername, String fileContent, String type) {Session targetSession = sessionMap.get(targetUsername);if (targetSession != null) {try {targetSession.getBasicRemote().sendText(type + "|" + fileContent); // 文件发送格式log.info("发送文件给用户:{},发送成功", targetUsername);} catch (IOException e) {log.error("发送文件失败:{}", e.getMessage());}}}/*** 发生错误时调用*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("发生错误,session:{} ,错误信息:{}", session.getId(), error);}/*** 服务端发送消息给指定用户* @param username 目标用户* @param message 消息内容*/public void sendMessageToUser(String username, String message, String type) {Session session = sessionMap.get(username);if (session != null && session.isOpen()) {try {session.getBasicRemote().sendText(type + "|" + message);log.info("发送给用户:{},内容:{}", username, message);} catch (IOException e) {log.error("发送消息失败:{}", e.getMessage());}} else {log.warn("用户:{} 不在线,无法发送消息", username);}}@Overridepublic void afterPropertiesSet() throws Exception {log.info("WebSocket服务端启动");}
}

  onopen方法主要用于连接的的方法,所有和websocket发起连接的客户端都会经过这个方法。

  onmessage方法主要用于发送消息的方法,其中定义了发送消息的格式,可以自行定义。

   前端代码:

 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>聊天界面</title><style>body { font-family: Arial, sans-serif; }#messages { border: 1px solid #ccc; height: 300px; overflow-y: scroll; margin-bottom: 10px; }input, button { margin: 5px; }</style>
</head>
<body>
<h2>聊天界面</h2>
<input type="text" id="targetUser" placeholder="输入目标用户名...">
<input type="text" id="message" placeholder="输入消息...">
<button id="sendBtn">发送</button>
<div id="messages"></div>
<img src="https://c-ssl.duitang.com/uploads/item/202003/27/20200327141738_ulbvu.jpg" alt="">
<script>const username = prompt("请输入您的用户名:"); // 获取当前用户的用户名const socket = new WebSocket(`ws://127.0.0.1:8088/singleChat/${username}`);socket.onopen = function() {console.log(`${username} 已连接`);};socket.onmessage = function(event) {const messagesDiv = document.getElementById("messages");messagesDiv.innerHTML += `<p>${event.data}</p>`;messagesDiv.scrollTop = messagesDiv.scrollHeight; // 滚动到底部};document.getElementById("sendBtn").onclick = function() {const targetUser = document.getElementById("targetUser").value;const messageInput = document.getElementById("message").value;const message = `${targetUser}:text:${messageInput}`; // 格式化消息socket.send(message);// 显示自己发送的消息const messagesDiv = document.getElementById("messages");messagesDiv.innerHTML += `<p>我: ${messageInput}</p>`;messagesDiv.scrollTop = messagesDiv.scrollHeight; // 滚动到底部document.getElementById("message").value = "";  // 清空输入框};
</script>
</body>
</html>

  

vue的部分代码和项目完整的截图为:

 this.socket = new WebSocket(`ws://127.0.0.1:8088/singleChat/${localStorage.getItem("username")}`);this.socket.onopen = () => {console.log(localStorage.getItem("username") + " 连接成功");};// 只设置一次 onmessage 处理逻辑this.socket.onmessage = (event) => {const message = event.data; // 假设格式为 "type:content"const parts = message.split("|"); // 按冒号分割if (parts.length === 2) {const type = parts[0].trim(); // 消息类型const content = parts[1].trim(); // 消息内容this.contactRecord.push({id: Date.now(), // 使用时间戳作为消息 IDsenderId: this.user.id, // 或者其他用户的 IDcontent: content,messageType: type, // 添加类型});// 进度条滚动到底部this.scrollToBottom();}};},

 

需要源码的请私信我:

谢谢各位的支持!!! 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot框架:大学城水电管理自动化

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

关于三色标记算法的理解

三色标记算法是一种垃圾标记的算法&#xff0c;用于cms和g1。 它将对象分为3种颜色&#xff1a; 1.白色对象&#xff1a;未被标记的对象 2.灰色对象&#xff1a;自身被标记&#xff0c;引用的其它对象还没被标记 3.黑色对象&#xff1a;自身以及所引用的对象都被标记完 标记过…

Python Matplotlib:基本图表绘制指南

Python Matplotlib&#xff1a;基本图表绘制指南 Matplotlib 是 Python 中一个非常流行的绘图库&#xff0c;它以简单易用和功能丰富而闻名&#xff0c;适合各种场景的数据可视化需求。在数据分析和数据科学领域&#xff0c;Matplotlib 是我们展示数据的有力工具。本文将详细讲…

深入探讨SEO分析技巧助力网站流量提升

内容概要 在当前的数字化时代&#xff0c;SEO分析的重要性不言而喻。它是提升网站流量的关键工具&#xff0c;帮助站长有效地优化网站内容和结构。通过系统的SEO分析&#xff0c;站长可以掌握用户搜索行为和需求&#xff0c;从而制定出更具针对性的内容策略。例如&#xff0c;…

配置QINQ

1. 配置公司A和公司B的私有网络&#xff0c;创建对应的VLAN&#xff0c;并且接口的链路类型 S3的配置: 系统视图进入&#xff1a; <Huawei>system-view 设置设备名称为s3&#xff1a; [huawei]sysname s3 创建VLAN 10和20&#xff1a; [s3]vlan batch 10 20 配置Gigabit…

react 中配置@寻找文件

安装插件craco npm i -D craco/craco 创建 craco.config.js文件放在根目录和package.json同级 const path require(path)module.exports {webpack: {alias: {"": path.resolve(__dirname, "src")}} }创建 jsconfig.config.js文件放在根目录和package.js…

基于Qt的独立线程创建与多线程执行实验Demo

一、多线程与线程池的应用目的[1][4] &#xff08;一&#xff09;多线程 一个进程内多个线程并发执行的情况就叫多线程&#xff0c;每一个线程是一个独立的执行流。多线程是一种编程模型&#xff0c;它与处理器无关&#xff0c;与设计机制有关。 需要多线程的原因包括&#xf…

电能质量治理产品在分布式光伏电站的应用

1.概述 随着全球对可再生能源需求的不断增长&#xff0c;分布式光伏电站的建设与扩张正迅速发展。然而&#xff0c;在其运行过程中&#xff0c;分布式光伏电站遭遇了一系列挑战&#xff0c;包括企业关口计量点功率因数降低和谐波污染等问题。这些问题不仅影响了光伏电站的运行…

如何解决导入aioredis报错TypeError: duplicate base class TimeoutError的问题(轻松解决,亲测有效)

下面是根据你的要求撰写的文章: 文章目录 📖 介绍 📖🏡 演示环境 🏡📒 aioredis导包报错 📒📝 解决方案📝 小贴士⚓️ 相关链接 ⚓️📖 介绍 📖 最近在使用Python异步redis模块aioredis的时候遇到了一个错误,导包报错提示 TypeError: duplicate base cla…

@Excel若依导出异常/解决BusinessBaseEntity里面的字段不支持导出

今天发现所有实体类继承BusinessBaseEntity里面的这些通用字段不支持导出&#xff0c;debug时发现是这样&#xff1a; 导出效果 这里我把能查到的方法都汇总了&#xff0c;如果你也遇到这个异常&#xff0c;可以去逐步排查 1.先看库里有没有数据 2.看字段名是否对齐 3.所需要…

vue组件在项目中的常用业务逻辑(2)

完成一个项目的模块总体分为四步&#xff1a; 一、先静态页面 静态组件拆分出来 二、发请求(API) 三、vuex三连环 1.导入api里的search模块请求 2.捞那个请求的数据 先用async和await 再传值给result&#xff0c;添加空对象&#xff0c;派发actions&#xff1a; 3.在mutatio…

气膜球幕展览馆:引领展示新风潮,震撼视界—轻空间

随着展览行业的不断发展&#xff0c;越来越多的创意场地应运而生&#xff0c;而气膜球幕展览馆凭借其独特的球形外观和创新的结构设计&#xff0c;迅速成为展览和活动行业中的新宠。无论是艺术展览、品牌展示&#xff0c;还是各种大型活动&#xff0c;气膜球幕展览馆都以其极具…

stm32 如何生成.bin文件-keil fromelf.exe使用

文章目录 一、fromelf.exe简介二、生成.bin文件设置工程&#xff1a;编译工程&#xff1a;配置fromelf.exe&#xff1a;重新编译&#xff1a; 三、fromelf.exe的其他功能四、使用注意事项五、总结 keil fromelf.exe使用 在Keil的开发流程中&#xff0c;fromelf.exe工具扮演了至…

【数据分析】如何构建指标体系?

有哪些指标体系搭建模型&#xff1f;五个步骤教你从0开始搭建指标体系 一、企业指标体系搭建存在什么问题 许多企业在搭建数据指标体系时遇到了诸多难题&#xff0c;如问题定位不准确、数据采集不完整、目标不一致、报表无序、指标覆盖不全面以及报表价值未充分利用等。 1、…

通过 ssh config 快速免密连接服务器

通过 ssh config 快速免密连接服务器 目录 通过 ssh config 快速免密连接服务器1. 创建ssh的私钥和公钥的话 &#xff08;如果已经做过可忽略&#xff09;2. 创建config文件&#xff0c;填写服务器配置3. 允许在远程服务器的授权密钥上安装 SSH 密钥 1. 创建ssh的私钥和公钥的话…

客户关系管理(CRM)是什么?CRM定义最全解读!

有企业就有业务&#xff0c;有业务就有客户&#xff0c;所以客户关系管理&#xff08;CRM&#xff09;系统是企业管理中不可或缺的一部分&#xff0c;它帮助企业有效管理与客户之间的关系&#xff0c;提升客户满意度和忠诚度。本文将重点探讨CRM的概念和不同的定义&#xff0c;…

lc 142 环形链表II

先判断存在环形链表&#xff0c;然后根据 x z&#xff0c;得到入口 /** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val x; * next null; * } * } */ public class Sol…

骑行,每天骑行多少最好?

热爱骑行的人都知道&#xff0c;每一次转动踏板都像是与风的对话&#xff0c;与大地的亲吻。我们校长骑行群的伙伴们&#xff0c;如同追逐自由的飞鸟&#xff0c;在骑行的道路上享受着速度与激情、宁静与思考。然而&#xff0c;一个看似简单却至关重要的问题常常萦绕在我们心头…

智能驾驶系列报告之一:高精度定位,智能驾驶的可靠辅助

城市导航辅助驾驶迅速落地&#xff0c;高精度定位迎来发展契机&#xff1a;首先&#xff0c;以城市 NOA为代表的 L3 级别功能正快速落地&#xff0c;智能驾驶已成各车企竞争的核心领域&#xff0c;搭载城市 NOA 功能的车型数量及渗透率快速提升。伴随电动化和智能化的提升&…