当前位置: 首页 > news >正文

SpringBoot+Vue 整合websocket实现简单聊天窗口

效果图

1 输入临时名字充当账号使用
image-1694448636449

2 进入聊天窗口
image-1694448674599

3 发送消息 (复制一个页面,输入其他名字,方便展示效果)
image-1694448766333

4 其他窗口效果
image-1694448783698

代码实现

后端SpringBoot项目,自行创建

pom依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.7.12</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version></dependency>

WebSocketConfig.java

package com.dark.wsdemo.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置类。开启WebSocket的支持*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

WebSocketServer.java

package com.dark.wsdemo.service;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dark.wsdemo.vo.MessageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** WebSocket的操作类*/
@Component
@Slf4j
@ServerEndpoint("/websocket/{name}")
public class WebSocketServer {/*** 静态变量,用来记录当前在线连接数,线程安全的类。*/private static final AtomicInteger onlineSessionClientCount = new AtomicInteger(0);/*** 存放所有在线的客户端*/private static final Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();/*** 连接 name 和连接会话*/private String name;@OnOpenpublic void onOpen(@PathParam("name") String name, Session session) {/*** session.getId():当前session会话会自动生成一个id,从0开始累加的。*/Session beforeSession = onlineSessionClientMap.get(name);if (beforeSession != null) {//在线数减1onlineSessionClientCount.decrementAndGet();log.info("连接已存在,关闭之前的连接 ==> session_id = {}, name = {}。", beforeSession.getId(), name);//通知之前其他地方连接被挤掉sendToOne(name, "您的账号在其他地方登录,您被迫下线。");// 从 Map中移除onlineSessionClientMap.remove(name);//关闭之前的连接try {beforeSession.close();} catch (Exception e) {log.error("关闭之前的连接异常,异常信息为:{}", e.getMessage());}}log.info("连接建立中 ==> session_id = {}, name = {}", session.getId(), name);onlineSessionClientMap.put(name, session);//在线数加1onlineSessionClientCount.incrementAndGet();this.name = name;sendToOne(name, "连接成功");log.info("连接建立成功,当前在线数为:{} ==> 开始监听新连接:session_id = {}, name = {}。", onlineSessionClientCount, session.getId(), name);}@OnClosepublic void onClose(@PathParam("name") String name, Session session) {if (name == null || name.equals("")) {name = this.name;}// 从 Map中移除onlineSessionClientMap.remove(name);//在线数减1onlineSessionClientCount.decrementAndGet();log.info("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:session_id = {}, name = {}。", onlineSessionClientCount, session.getId(), name);}@OnMessagepublic void onMessage(String message, Session session) {JSONObject jsonObject = JSON.parseObject(message);String toname = jsonObject.getString("name");String msg = jsonObject.getString("message");log.info("服务端收到客户端消息 ==> fromname = {}, toname = {}, message = {}", name, toname, message);/*** 模拟约定:如果未指定name信息,则群发,否则就单独发送*/if (toname == null || toname == "" || "".equalsIgnoreCase(toname)) {sendToAll(msg);} else {sendToOne(toname, msg);}}/*** 发生错误调用的方法** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("WebSocket发生错误,错误信息为:" + error.getMessage());error.printStackTrace();}/*** 群发消息** @param message 消息*/private void sendToAll(String message) {// 遍历在线map集合onlineSessionClientMap.forEach((onlineName, toSession) -> {// 排除掉自己if (!name.equalsIgnoreCase(onlineName)) {log.info("服务端给客户端群发消息 ==> name = {}, toname = {}, message = {}", name, onlineName, message);MessageVo messageVo = new MessageVo();messageVo.setFrom(name);messageVo.setDate(new Date());messageVo.setMessage(message);toSession.getAsyncRemote().sendText(JSON.toJSONString(messageVo));}});}/*** 指定发送消息** @param toName* @param message*/private void sendToOne(String toName, String message) {// 通过name查询map中是否存在Session toSession = onlineSessionClientMap.get(toName);if (toSession == null) {log.error("服务端给客户端发送消息 ==> toname = {} 不存在, message = {}", toName, message);return;}// 异步发送log.info("服务端给客户端发送消息 ==> toname = {}, message = {}", toName, message);MessageVo messageVo = new MessageVo();messageVo.setFrom(name);messageVo.setDate(new Date());messageVo.setMessage(message);toSession.getAsyncRemote().sendText(JSON.toJSONString(messageVo));}}

MessageVo.java

package com.dark.wsdemo.vo;import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;import java.util.Date;@Data
public class MessageVo {private String from;//json时候格式化为时间格式@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date date;private String message;
}

Vue代码实现

App.vue

<template><div id="app"><!-- Modal Dialog --><div class="modal" v-if="!username"><div class="modal-content"><h2>请输入你的名字</h2><input type="text" v-model="inputUsername" /><button @click="setUsername">确定</button></div></div><!-- Chat Box --><div class="chat-box" v-if="username"><div class="chat-history"><div v-for="msg in messages" :key="msg.id" :class="[msg.type, 'message']"><div class="info"><span class="from">{{ msg.from }}</span><span class="date">{{ msg.date }}</span></div><div class="bubble">{{ msg.message }}</div></div></div><div class="chat-input"><input type="text" v-model="inputMessage" @keyup.enter="sendMessage" placeholder="请输入消息..."/><button @click="sendMessage">发送</button></div></div></div>
</template><script>
export default {data() {return {inputMessage: '',inputUsername: '',messages: [],username: '',ws: null,};},methods: {setUsername() {if (this.inputUsername.trim() === '') return;this.username = this.inputUsername.trim();this.ws = new WebSocket(`ws://localhost:8081/websocket/${this.username}`);this.ws.addEventListener('message', (event) => {const data = JSON.parse(event.data);this.messages.push({ ...data, type: 'left', id: this.messages.length });});},sendMessage() {if (this.inputMessage.trim() === '') return;const message = {from: this.username,date: new Date().toLocaleString(),message: this.inputMessage.trim(),};this.ws.send(JSON.stringify(message));this.messages.push({ ...message, type: 'right', id: this.messages.length });this.inputMessage = '';},},
};
</script><style>
/* Modal Styles */
.modal {display: flex;justify-content: center;align-items: center;position: fixed;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0, 0, 0, 0.5);z-index: 9999;
}.modal-content {background-color: #fff;padding: 20px;width: 300px;text-align: center;border-radius: 10px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}/* Chat Box Styles */
#app {background-color: #f2f2f2;display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;font-family: Arial, sans-serif;
}.chat-box {box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);width: 300px;height: 400px;border-radius: 8px;overflow: hidden;display: flex;flex-direction: column;
}.chat-history {flex: 1;overflow-y: auto;padding: 10px;background-color: #fff;
}.message {padding: 5px 0;
}.info {font-size: 12px;color: gray;margin-bottom: 4px;
}.left .bubble {background-color: #e6e6e6;border-radius: 15px;padding: 12px;display: inline-block;
}.right .bubble {background-color: #007bff;color: white;border-radius: 15px;padding: 12px;display: inline-block;margin-left: auto;
}.chat-input {display: flex;padding: 10px;background-color: #f7f7f7;border-top: 1px solid #ccc;
}input {flex: 1;padding: 8px;border: 1px solid #ccc;border-radius: 4px;margin-right: 10px;
}button {padding: 10px 20px;background-color: #007bff;color: white;border: none;border-radius: 4px;cursor: pointer;
}button:hover {background-color: #0056b3;
}
</style>

相关文章:

SpringBoot+Vue 整合websocket实现简单聊天窗口

效果图 1 输入临时名字充当账号使用 2 进入聊天窗口 3 发送消息 &#xff08;复制一个页面&#xff0c;输入其他名字&#xff0c;方便展示效果&#xff09; 4 其他窗口效果 代码实现 后端SpringBoot项目&#xff0c;自行创建 pom依赖 <dependency><groupId…...

PCB layout在布线上的设计规范有哪些?

PCB Layout是一项技术活&#xff0c;也是经验活&#xff0c;良好的PCB Layout布线可帮助工程师确保最终的电路板性能、可靠性和制造质量&#xff0c;因此是很多电子工程师的学习重点&#xff0c;下面我们来盘点下PCB Layout关于布线的规范有哪些。 1、地管的引脚接地越短越好&a…...

喜报丨迪捷软件入选浙江省2023年省级产业数字化服务商

近日&#xff0c;根据《关于组织开展2023年度省级产业数字化服务商申报工作的通知》要求&#xff0c;省经信厅公布2023年省级产业数字化服务商名单&#xff0c;浙江迪捷软件科技有限公司榜上有名。 省级产业数字化服务商上榜名单的评选在企业申报、地方推荐、专家评审、综合评估…...

verilog写rom,采用端口排序顺序例化

verilog写rom,采用端口排序顺序例化 1,介绍rom,以及rom与ram的区别2,RTL设计模块、门级网表以及testbench测试模块2.1 RTL设计2.2 门级网表2.3 testbench3,波形输出1,介绍rom,以及rom与ram的区别 参考文献: 1, 转载-ROM、RAM存储器原理详解以及DRAM、SRAM、SDRAM 、FLA…...

基于SSM的共享客栈管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…...

全屏Activity弹出键盘不顶起布局

最近遇到的一个问题是全屏Activity中要求弹出键盘不顶起布局&#xff0c;首先windowSoftInputMode的取值是有多个的&#xff0c;在全屏场景下adjustPan是没有用的&#xff0c;需要使用adjustResize首先确保键盘不顶起布局。 android:windowSoftInputMode"stateHidden|adju…...

JAVA设计模式详解 解构设计模式思想 详细代码对比

JAVA设计模式详解 1 简单工厂模式 1 简单工厂模式 设计模式-01简单工厂模式详解 详细代码对比...

lintcode 567 · 最大得分 【动态规划 中等 】

题目 https://www.lintcode.com/problem/567 给定一个矩阵matrix&#xff0c; matrix[i][j]表示你到达第i行第j列可以得到的分数&#xff0c;现在你要用第0行任意一点出发&#xff0c;从每行里找到一个点进行跳跃&#xff0c;每次从(i,j)到(i1,k)跳跃需要消耗∣j−k∣的分数&…...

qml嵌入到QWidget的两种方式介绍

本文介绍qml页面嵌入到QWidget的两种方式,以及这两种方式的区别。 方式1 在 Qt 中,可以使用 QQuickWidget 将 QML 内容嵌入到基于 QWidget 的应用程序中。这是在旧的 QWidget-based 应用程序中逐渐引入 QML UI 的一种常见方式。 以下是如何使用 QQuickWidget 将 QML 内容嵌…...

Mysql数据库之常用SQL语句及事务学习总结

数据库介绍 几个常见的缩写&#xff1a; DB&#xff1a;数据库。全称&#xff1a;DataBase。DBMS&#xff1a;数据库管理系统。全称&#xff1a;DataBase Management System。DBS&#xff1a;数据库系统。全称&#xff1a;DataBase System。DBA&#xff1a;数据库管理员。全称…...

RuoYi若依管理系统最新版 基于SpringBoot的权限管理系统

RuoYi是一个后台管理系统&#xff0c;基于经典技术组合&#xff08;Spring Boot、Apache Shiro、MyBatis、Thymeleaf&#xff09;主要目的让开发者注重专注业务&#xff0c;降低技术难度&#xff0c;从而节省人力成本&#xff0c;缩短项目周期&#xff0c;提高软件安全质量。 本…...

html实现邮件模版布局-flex布局table布局-demo

邮件模版布局 flex - 布局简单方便 兼容性差 table - 优点 就是兼容性好&#xff0c;其他没有优点 注&#xff1a;使用图片需要png最好&#xff0c;使用svg图google邮箱会出现不能使用的情况 效果图 flex布局 <!DOCTYPE html> <html lang"en" xmlns:th&qu…...

CENTOS7安装redis在/home/pms/software路径下,并且将redis加入到systemctl中

要将/home/software/redis-stack-server-7.2.0-v0/service/redis.service添加到systemctl系统管理&#xff0c;你可以执行以下步骤&#xff1a; 创建软连接&#xff1a; sudo ln -s /home/software/redis-stack-server-7.2.0-v0/service/redis.service /etc/systemd/system/r…...

数据库笔记

数据库原理及应用 半期考&#xff1a;运筹学&#xff0c;概率论&#xff0c;数据库 文章目录 数据库原理及应用1.课程的考核2.数据库的运用3.数据库学什么&#xff1f; 第一章 绪论1.1数据库系统概述1.1.1基本概念1.1.2数据管理技术的生产和发展人工管理文件系统数据库系统 1.…...

AI是风口还是泡沫?

KlipC报道&#xff1a;狂热的人工智能追捧潮有所冷静&#xff0c;投资者在“上头”的追涨之后&#xff0c;开始回归到对基本面的关注。 KlipC的合伙人Andi D表示&#xff1a;“近日&#xff0c;有关英伟达二季度“破纪录”财报涉嫌造假的话题正在社交媒体和投资者论坛中甚嚣尘上…...

echarts环图配置

echarts环图配置 1、安装echarts npm install echarts4.9.02、页面引入echarts import echarts from echarts;3、应用 template片段 <div class"chart-wrap"><div id "treeChart" style "width: 180px; height:180px;" ><…...

Redis优化 RDB AOF持久化

---------------------- Redis 高可用 ---------------------------------------- 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redis语境…...

三维模型3DTILE格式轻量化压缩主要技术方法浅析

三维模型3DTILE格式轻量化压缩主要技术方法浅析 三维模型3DTILE格式轻量化压缩主要技术方法浅析 随着三维地理空间数据的应用日益广泛&#xff0c;为了更快速地传输和存储这些大规模数据&#xff0c;3DTile格式的轻量化压缩显得尤为重要。本文将浅析关于三维模型3DTile格式轻量…...

c++day2---9.7

1> 思维导图 2> 封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个公有成员…...

地震反演基础知识2(代码演示)

文章目录 数据集代码演示1. SEG盐真实数据2. SEG盐速度模型3. SEG盐模拟地震数据4. SEG盐模拟速度模型5. openfwi地震数据6. openfwi速度模型 数据集代码演示 1. SEG盐真实数据 # 绘制SEG盐层数据的地震图像 def pain_seg_seismic_data(para_seismic_data):Plotting seismic …...

C#学习 - 方法的定义、调用、调试

方法 方法&#xff08;Method&#xff09;是由C/C中的函数&#xff08;Function&#xff09;发展而来的 //C语言 #include <stdio.h> int Add(int x, int y) {return x y; }//函数 int main(void) {int a 4;int b 2;int c Add(a, b);printf("%d %d %d\n&quo…...

『PyQt5-Qt Designer篇』| 09 Qt Designer中分割线和间隔如何使用?

09 Qt Designer中分割线和间隔如何使用? 1 间隔1.1 水平间隔1.2 垂直间隔2 分割线2.1 水平线2.2 垂直线3 保存并执行1 间隔 间隔有水平间隔和垂直间隔: 1.1 水平间隔 拖动4个按钮,并设置为水平布局: 在第一个按钮的右边添加一个水平间隔: 设置其sizeType为Fixed,宽度为20…...

基于springboot2+mybatis-plus+jsp增删改查

概述 编写简单增删改查&#xff0c;理解之后可以自己试着扩展&#xff0c;相信你也可以&#xff0c;加油&#xff0c;我自己懂了的用注释记在下面方便理解 详细 一、需求&#xff08;要做什么&#xff09; 基于现今最流行的技术实现增删改查demo&#xff0c; 便于初学者上手…...

[PHP]empty一直返回true

class Post {public function __get($key){return true;} }$post new Post(); var_dump(empty($post->a));// bool(true) PHP: 重载 - Manual 读取不可访问&#xff08;protected 或 private&#xff09;或不存在的属性的值时&#xff0c;__get() 会被调用。 当对不可访…...

[2023.09.11]: Yew的SSR中的Cargo.toml配置

由于各种原因&#xff0c;我最后还是打算把Yew应用的开发从csr模式转成ssr模式。没想到这里面的水还是挺深的&#xff0c;这里面的Cargo.toml配置包含的信息量之大&#xff0c;着实让我头疼了一番。 Cargo.toml的配置如下 [package] name "app" version "0.…...

HTTPS加密协议详解:HTTPS性能与优化

1、HTTPS性能损耗 前文讨论了HTTPS原理与优势&#xff1a;身份验证、信息加密与完整性校验等&#xff0c;且未对TCP和HTTP协议做任何修改。但通过增加新协议以实现更安全的通信必然需要付出代价&#xff0c;HTTPS协议的性能损耗主要体现如下&#xff1a; (1).增加延时 分析前…...

9月11日,每日信息差

今天是2023年09月11日&#xff0c;以下是为您准备的13条信息差 第一、微软已停止向俄罗斯提供服务&#xff0c;俄罗斯接下来的举动震惊世人&#xff01;对此俄罗斯回应称&#xff0c;他们将把微软的收费版改为免费版并推广至全球 第二、我国首套海洋漂浮式温差能发电装置完成…...

RHCSA-VM-Linux基础配置命令

1.代码命令 1.查看本机IP地址&#xff1a; ip addr 或者 ip a [foxbogon ~]$ ip addre [foxbogon ~]$ ip a 1&#xff1a;<Loopback,U,LOWER-UP> 为环回2网卡 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP>为虚拟机自身网卡 2.测试网络联通性&#xff1a; [f…...

Web安全研究(四)

No Honor Among Thieves: A Large-Scale Analysis of Malicious Web Shells Stony Brook University Ruhr-University Bochum Web shell作为恶意脚本&#xff0c;攻击者将其上传到被攻陷的Web服务器&#xff0c;以远程执行任意命令、维护其访问权限并提升其特权。尽管在实践中它…...

【CS324】Large Language Models(持续更新)

note 文章目录 note一、引言二、大模型的能力三、大模型的有害性&#xff08;上&#xff09;四、大模型的有害性&#xff08;下&#xff09;五、大模型的数据Reference 一、引言 语言模型最初是在信息理论的背景下研究的&#xff0c;可以用来估计英语的熵。 熵用于度量概率分布…...

西安网站建设首选/优化系统的软件

原标题&#xff1a;「数控干货」基于UG CLS文件使用 C 语言制作智能后处理工具1 前言UG 后处理操作是 UGCAM 数控加工工作中一个重要环节&#xff0c;主要任务是把在 UG 加工环境下生成的加工刀位文件转换成机床可接受的数控代码文件。UG 本身提供了强大的 Post Builder 后处理…...

恋爱ppt模板免费下载网站/seo网站优化外包

http://www.cnblogs.com/sfger/p/3335672.html 原因是安装在有空格的目录下会出现以下错误&#xff1a; 经过头疼的排查后发现问题出现在&#xff1a;set diffexprMyDiff() 原来vim的自带的MyDiff函数有问题&#xff0c; 在_vimrc或.vimrc中找到下面代码&#xff1a; if &am…...

wordpress网站源码分享/哈尔滨seo关键词优化

很多时候&#xff0c;我们需要自己去定义dialog&#xff0c;目前我们就遇见了这样一个需求&#xff0c;我的想法是自己定义一个dialog&#xff0c;如果有list的话就使用listview&#xff0c;如果有msg的话就使用msg&#xff0c;并且取消和确定按钮也可自己定义。自定义一个dial…...

北京展览馆网站建设/免费的行情网站app

你有没有碰到过OpenStack中&#xff0c;VM失去IP地址的问题&#xff1f;如果有的话&#xff0c;你知道那可能是什么问题 ——特别是如果你拥有大量的节点和VM。你的客户会因为没有明显原因却断了与VM的连接而感到 挫败。甚至云的支持团队会为log文件里没有提示却出现问题感到挫…...

嘉兴网站制作价格/最新旅游热点

题目链接&#xff1a; 4517: [Sdoi2016]排列计数 Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 846 Solved: 530[Submit][Status][Discuss]Description 求有多少种长度为 n 的序列 A&#xff0c;满足以下条件&#xff1a;1 ~ n 这 n 个数在序列中各出现了一次若第 i 个数…...

中国建设银行网站会员登录/腾讯云域名注册官网

如何为企业量身制定行之有效的考核体系&#xff1f;如何有效激发员工的创业激情&#xff1f;如何通过企业绩效管理助推集团战略落地&#xff1f;阿米巴通过“81”绩效量化技术&#xff0c;解决了“考核什么”和“怎么考核”的技术问题&#xff0c;让员工不但要对过程负责&#…...