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.mzph.cn/diannao/59782.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【D3.js in Action 3 精译_038】4.2 D3 折线图的绘制方法及曲线插值处理

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

Git 的分支管理

一、分支介绍 1、分支是什么 Git作为一个分布式版本控制系统&#xff0c;提供了强大而灵活的分支管理功能&#xff0c;使得开发团队能够高效地协作开发、管理不同的功能和版本。 2、为什么有分支 一般情况下主分支&#xff08;master/main&#xff09;应始终保持可部署的状…

Linux环境基础和基础开发工具使用

文章目录 一、yum软件管理器1、包管理器2、yum3、apt4、安装源 二、编辑器vim1、各种模式2、打开时直接让光标定位到指定号3、&#xff01;加命令字符 三、命令模式1、i 进入插入模式2、**Shift :** 进入底行模式3、光标定位4、ZZ&#xff08;大写&#xff09;保存并退出vim5、…

【java】哈希<两数之和> 理解哈希

两数之和 题目描述&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你…

【Linux】信号三部曲——产生、保存、处理

信号 1. 信号的概念2. 进程如何看待信号3. 信号的产生3.1. kill命令3.2. 终端按键3.2.1. 核心转储core dump3.2.2. OS如何知道键盘在输入数据 3.3. 系统调用3.3.1. kill3.3.2. raise3.3.3. abort 3.4. 软件条件3.4.1. SIGPIPE信号3.4.2. SIGALRM信号 3.5. 硬件异常3.5.1. 除零异…

Vue 计算属性和监听器

文章目录 一、计算属性1. 计算属性定义2. computed 比较 methods3. 计算属性完整写法 二、监听器1. 普通监听2. 添加额外配置项 一、计算属性 1. 计算属性定义 概念&#xff1a;基于现有的数据&#xff0c;计算出来的新属性&#xff0c;依赖的数据变化&#xff0c;自动重新计…

【计网】实现reactor反应堆模型 --- 框架搭建

没有一颗星&#xff0c; 会因为追求梦想而受伤&#xff0c; 当你真心渴望某样东西时&#xff0c; 整个宇宙都会来帮忙。 --- 保罗・戈埃罗 《牧羊少年奇幻之旅》--- 实现Reactor反应堆模型 1 前言2 框架搭建3 准备工作4 Reactor类的设计5 Connection连接接口6 回调方法 1 …

外包干了2年,快要废了。。。

先说一下自己的情况&#xff0c;普通本科毕业&#xff0c;在外包干了2年多的功能测试&#xff0c;这几年因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不能够在这样蹉跎下去了&#xff0c;长时间呆在一个舒适的环境真的会…

linux驱动-i2c子系统框架学习(2)

linux驱动-i2c子系统框架学习(1) 在这篇博客里面已经交代了i2c设备驱动层&#xff0c;主要的功能就是编写具体i2c的外设驱动&#xff0c;和创建设备接点给上层使用 &#xff0c;按之前学习的字符设备&#xff0c;有了设备节点&#xff0c;就可以对硬件操作了&#xff0c;在i2c…

Webserver(4.6)poll和epoll

目录 pollclient.cpoll.c epollepoll.cclient.c epoll的两种工作模式水平触发边沿触发 poll poll是对select的一个改进 select的缺点在于每次都需要将fd集合从用户态拷贝到内核态&#xff0c;开销很大。每次调用select都需要在内核遍历传递进来的所有fd&#xff0c;这个开销也…

Stable Diffusion的解读(一)

Stable Diffusion的解读&#xff08;一&#xff09; 文章目录 Stable Diffusion的解读&#xff08;一&#xff09;摘要Abstract一、机器学习部分1. Stable Diffusion的早期工作1.1 从编码器谈起1.2 第一条路线&#xff1a;VAE和DDPM1.3 第二条路线&#xff1a;VQVAE1.4 路线的交…

计算机网络——TCP篇

TCP篇 基本认知 TCP和UDP的区别? TCP 和 UDP 可以使用同一个端口吗&#xff1f; 可以的 传输层中 TCP 和 UDP在内核中是两个完全独立的软件模块。可以根据协议字段来选择不同的模块来处理。 TCP 连接建立 TCP 三次握手过程是怎样的&#xff1f; 一次握手:客户端发送带有 …

ROS话题通信机制理论模型的学习

话题通信是ROS&#xff08;Robot Operating System&#xff0c;机器人操作系统&#xff09;中使用频率最高的一种通信模式&#xff0c;其实现模型主要基于发布/订阅模式。 一、基本概念 话题通信模型中涉及三个主要角色&#xff1a; ROS Master&#xff08;管理者&#xff0…

【Android】名不符实的Window类

1.“名不符实”的Window类 Window 是一个窗口的概念&#xff0c;是所有视图的载体&#xff0c;不管是 Activity&#xff0c;Dialog&#xff0c;还是 Toast&#xff0c;他们的视图都是附加在 Window 上面的。例如在桌面显示一个悬浮窗&#xff0c;就需要用到 Window 来实现。Wi…

后端java——如何为你的网页设置一个验证码

目录 1、工具的准备 2.基本方法 3.实现类 4.实践 HTML文件&#xff1a; Java文件1:创建验证码 Java文件2:验证验证码 本文通过HUTOOL实现&#xff1a;Hutool参考文档Hutool&#xff0c;Java工具集https://hutool.cn/docs/#/ 1、工具的准备 如果我们通过hutool来实现这个…

【go从零单排】Strings and Runes 字符串和字符

Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 概念 在Go语言中&#xff0c;rune 是一个内置的数据类型&#xff0c;用于表示一个Unicode字符。它实际上是一个别名…

如何在本地Linux服务器搭建WordPress网站结合内网穿透随时随地可访问

文章目录 前言1. 安装WordPress2. 创建WordPress数据库3. 安装相对URL插件4. 安装内网穿透发布网站4.1 命令行方式&#xff1a;4.2. 配置wordpress公网地址 5. 配置WordPress固定公网地址 前言 本文主要介绍如何在Linux Ubuntu系统上使用WordPress搭建一个本地网站&#xff0c…

vue data变量之间相互赋值或进行数据联动

摘要&#xff1a; 使用vue时开发会用到data中是数据是相互驱动&#xff0c;经常会想到watch,computed&#xff0c;总结一下&#xff01; 直接赋值&#xff1a; 在 data 函数中定义的变量可以直接在方法中进行赋值。 export default {data() {return {a: 1,b: 2};},methods: {u…

在 Java 中使用脚本语言

在 Java 中使用脚本语言&#xff0c;特别是在 Java 平台上集成如 Python、JavaScript 或 Ruby 等语言&#xff0c;通常可以通过 Java 的 Scripting API 来实现。这个 API 基于 JSR 223&#xff08;“Scripting for the Java Platform”&#xff09;&#xff0c;提供了一种标准方…

大数据-212 数据挖掘 机器学习理论 - 无监督学习算法 KMeans 基本原理 簇内误差平方和

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…