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

Spring boot使用websocket实现在线聊天

maven依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.26</version></dependency>

Java 类

WebSocket 配置

package com.zpjiang.chat;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {/*** 	注入ServerEndpointExporter,* 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

WebSocket 接口入径(ws://localhost:8087/socket/websocket/userId)

package com.zpjiang.chat;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;import org.springframework.stereotype.Component;import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}") // 接口路径  ws://localhost:8087/socket/websocket/userId;
public class WebSocket {// 与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 用户ID*/private String userId;// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。// 虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。// 注:底下WebSocket是当前类名private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();// 用来存在线连接用户信息private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value = "userId") String userId) {try {this.session = session;this.userId = userId;webSockets.add(this);sessionPool.put(userId, session);log.info("【websocket消息】有新的连接,总数为:" + webSockets.size());} catch (Exception e) {}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.userId);log.info("【websocket消息】连接断开,总数为:" + webSockets.size());} catch (Exception e) {}}/*** 收到客户端消息后调用的方法** @param message* @param session*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:" + message);Message msg = JSONUtil.toBean(message, Message.class);sendOneMessage(msg.getToUID(), message);}/*** 发送错误时的处理* * @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:" + error.getMessage());error.printStackTrace();}// 此为广播消息public void sendAllMessage(String message) {log.info("【websocket消息】广播消息:" + message);for (WebSocket webSocket : webSockets) {try {if (webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}// 此为单点消息public void sendOneMessage(String userId, String message) {Session session = sessionPool.get(userId);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}// 此为单点消息(多人)public void sendMoreMessage(String[] userIds, String message) {for (String userId : userIds) {Session session = sessionPool.get(userId);if (session != null && session.isOpen()) {try {log.info("【websocket消息】 单点消息:" + message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}}

controller

package com.zpjiang.chat;import javax.annotation.Resource;import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;@RestController
public class WebsocketController {@Resourceprivate WebSocket webSocket;@GetMapping("/page")public ModelAndView page() {return new ModelAndView("webSocket");}@RequestMapping("/push/{toUID}/{msg}")public ResponseEntity<String> pushToClient(@PathVariable("msg")String message, @PathVariable("toUID") String toUID) throws Exception {webSocket.sendOneMessage(toUID, message);return ResponseEntity.ok("Send Success!");}
}

消息bean

package com.zpjiang.chat;import lombok.Data;
import lombok.Getter;
import lombok.Setter;@Data
@Getter
@Setter
public class Message {private String toUID;private String Msg;
}

main方法类

package com.zpjiang.chat;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class ChatApplication {public static void main(String[] args) {SpringApplication.run(ChatApplication.class, args);}
}

配置文件

server:port: 8087servlet:context-path: /socketspring:application:name: websocket

视图文件 /src/main/resources/templates/webSocket.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket消息通知</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>var socket;//打开WebSocketfunction openSocket() {if (typeof (WebSocket) === "undefined") {console.log("您的浏览器不支持WebSocket");} else {console.log("您的浏览器支持WebSocket");//实现化WebSocket对象,指定要连接的服务器地址与端口,建立连接.//ws://localhost:8087/socket/websocket/userIdvar socketUrl = "http://localhost:8087/socket/websocket/"+ $("#uid").val();//将https与http协议替换为ws协议socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");console.log(socketUrl);if (socket != null) {socket.close();socket = null;}socket = new WebSocket(socketUrl);//打开事件socket.onopen = function() {console.log("WebSocket已打开");//socket.send("这是来自客户端的消息" + location.href + new Date());};//获得消息事件socket.onmessage = function(msg) {console.log(msg.data);//发现消息进入,开始处理前端触发逻辑};//关闭事件socket.onclose = function() {console.log("WebSocket已关闭");};//发生了错误事件socket.onerror = function() {console.log("WebSocket发生了错误");}}}var socket1;//打开WebSocketfunction openSocket1() {if (typeof (WebSocket) === "undefined") {console.log("您的浏览器不支持WebSocket");} else {console.log("您的浏览器支持WebSocket");//实现化WebSocket对象,指定要连接的服务器地址与端口,建立连接.//ws://localhost:8087/socket/websocket/userIdvar socketUrl = "http://localhost:8087/socket/websocket/"+ $("#toUID").val();//将https与http协议替换为ws协议socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");console.log(socketUrl);if (socket1 != null) {socket1.close();socket1 = null;}socket1 = new WebSocket(socketUrl);//打开事件socket1.onopen = function() {console.log("WebSocket已打开");//socket.send("这是来自客户端的消息" + location.href + new Date());};//获得消息事件socket1.onmessage = function(msg) {console.log("socket1收到消息:");console.log(msg.data);//发现消息进入,开始处理前端触发逻辑};//关闭事件socket1.onclose = function() {console.log("WebSocket已关闭");};//发生了错误事件socket1.onerror = function() {console.log("WebSocket发生了错误");}}}//发送消息function sendMessage() {if (typeof (WebSocket) === "undefined") {console.log("您的浏览器不支持WebSocket");} else {console.log("您的浏览器支持WebSocket");console.log('{"toUID":"' + $("#toUID").val() + '","Msg":"'+ $("#msg").val() + '"}');socket.send('{"toUID":"' + $("#toUID").val() + '","Msg":"'+ $("#msg").val() + '"}');}}function sendMessage1() {if (typeof (WebSocket) === "undefined") {console.log("您的浏览器不支持WebSocket");} else {console.log("您的浏览器支持WebSocket");console.log('{"toUID":"' + $("#uid").val() + '","Msg":"'+ $("#msg").val() + '"}');socket1.send('{"toUID":"' + $("#uid").val() + '","Msg":"'+ $("#msg").val() + '"}');}}
</script>
<body><p>【uid】:<div><input id="uid" name="uid" type="text" value="1"></div><p>【toUID】:<div><input id="toUID" name="toUID" type="text" value="2"></div><p>【Msg】:<div><input id="msg" name="msg" type="text" value="hello WebSocket2"></div><p>【第一步操作:】:<div><button onclick="openSocket()">开启socket</button><button onclick="openSocket1()">开启socket2</button></div><p>【第二步操作:】:<div><button onclick="sendMessage()">发送消息</button><button onclick="sendMessage1()">发送消息2</button></div>
</body></html>

相关文章:

Spring boot使用websocket实现在线聊天

maven依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spr…...

品牌设计理念和logo设计方法

一 品牌设计的目的 设计是为了传播&#xff0c;让传播速度更快&#xff0c;传播效率更高&#xff0c;减少宣传成本 二 什么是好的品牌设计 好的设计是为了让消费者更容易看懂、记住的设计&#xff0c; 从而辅助传播&#xff0c; 即 看得懂、记得住。 1 看得懂 就是让别人看懂…...

Python | Leetcode Python题解之第88题合并两个有序数组

题目&#xff1a; 题解&#xff1a; class Solution:def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:"""Do not return anything, modify nums1 in-place instead."""p1, p2 m - 1, n - 1tail m n - 1whi…...

vscode新版本remotessh服务端报`GLIBC_2.28‘ not found解决方案

问题现象 通过vscode的remotessh插件连接老版本服务器&#xff08;如RHEL7&#xff0c;Centos7&#xff09;时&#xff0c;插件会报错&#xff0c;无法连接。 查看插件的错误日志可以看到类似如下的报错信息&#xff1a; dc96b837cf6bb4af9cd736aa3af08cf8279f7685/node: /li…...

盘他系列——oj!!!

1.Openjudge 网站: OpenJudge 2.洛谷 网站: 首页 - 洛谷 | 计算机科学教育新生态 3.环球OJ 网站: QOJ - QOJ.ac 4. 北京大学 OJ:Welcome To PKU JudgeOnline 5.自由OJ 网站: https://loj.ac/ 6.炼码 网站:LintCode 炼码 8.力扣 网站: 力扣 9.晴练网首页 - 晴练网...

洛谷 P2657 [SCOI2009] windy 数 题解 数位dp

[SCOI2009] windy 数 题目背景 windy 定义了一种 windy 数。 题目描述 不含前导零且相邻两个数字之差至少为 2 2 2 的正整数被称为 windy 数。windy 想知道&#xff0c;在 a a a 和 b b b 之间&#xff0c;包括 a a a 和 b b b &#xff0c;总共有多少个 windy 数&…...

Python爬虫入门:网络世界的宝藏猎人

今天阿佑将带你踏上Python的肩膀&#xff0c;成为一名网络世界的宝藏猎人&#xff01; 文章目录 1. 引言1.1 简述Python在爬虫领域的地位1.2 阐明学习网络基础对爬虫的重要性 2. 背景介绍2.1 Python语言的流行与适用场景2.2 网络通信基础概念及其在数据抓取中的角色 3. Python基…...

【NodeMCU实时天气时钟温湿度项目 6】解析天气信息JSON数据并显示在 TFT 屏幕上(心知天气版)

今天是第六专题&#xff0c;主要内容是&#xff1a;导入ArduinoJson功能库&#xff0c;借助该库解析从【心知天气】官网返回的JSON数据&#xff0c;并显示在 TFT 屏幕上。 如您需要了解其它专题的内容&#xff0c;请点击下面的链接。 第一专题内容&#xff0c;请参考&a…...

重构四要素:目的、对象、时机和方法

目录 1.引言 2.重构的目的:为什么重构(why) 3.重构的对象:到底重构什么(what) 4.重构的时机:什么时候重构(when) 5.重构的方法:应该如何重构(how) 6.思考题 1.引言 一些软件工程师对为什么要重构(why)、到底重构什么(what)、什么时候重构(when)应该如何重构(how)等问题的…...

基于Echarts的大数据可视化模板:服务器运营监控

目录 引言背景介绍研究现状与相关工作服务器运营监控技术综述服务器运营监控概述监控指标与数据采集可视化界面设计与实现数据存储与查询优化Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满足管理的特定需求模板功能与特性…...

Python3 笔记:Python的常量

常量&#xff08;constant&#xff09;&#xff1a;跟变量相对应&#xff0c;指第一次赋予值后就保持固定不变的值。 Python里面没有声明常量的关键字&#xff0c;其他语言像C/C/Java会有const修饰符&#xff0c;但Python没有。 Python中没有使用语法强制定义常量&#xff0c…...

【Linux】自动化构建工具make/Makefile和git介绍

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12625432.html 目录 前言 Linux项目自动化构建工具-make/Makefile 举例 .PHONY 常见符号 依赖关系…...

C语言—关于字符串(编程实现部分函数功能)

0.前言 当我们使用这些函数功能时&#xff0c;可以直接调用头文件---#include<string.h>&#xff0c;然后直接使用就行了,本文只是手动编写实现函数的部分功能 1.strlen函数功能实现 功能说明&#xff1a;strlen(s)用来计算字符串s的长度&#xff0c;该函数计数不会包括最…...

picoCTF-Web Exploitation-Trickster

Description I found a web app that can help process images: PNG images only! 这应该是个上传漏洞了&#xff0c;十几年没用过了&#xff0c;不知道思路是不是一样的&#xff0c;以前的思路是通过上传漏洞想办法上传一个木马&#xff0c;拿到webshell&#xff0c;今天试试看…...

SSH 免密登录,设置好仍然需要密码登录解决方法

说明&#xff1a; ssh秘钥登录设置好了&#xff0c;但是登录的时候依然需要提供密码 查看系统安全日志&#xff0c;定位问题 sudo cat /var/log/auth.log或者 sudo cat /var/log/secure找到下面的信息 Authentication refused: bad ownership or modes...&#xff08;网上的…...

【斑马打印机】web前端页面通过BrowserPrint API连接斑马打印机进行RFID条形码贴纸打印

web前端页面通过BrowserPrint API连接斑马打印机进行RFID条形码贴纸打印 在现代物流、仓储和零售行业中,RFID和二维码技术发挥着至关重要的作用。这些技术不仅提高了效率,还增强了追踪和管理的能力。本文将介绍如何使用JavaScript和斑马打印机的BrowserPrint API来打印RFID二…...

DigitalOcean 应用托管更新:应用端到端运行时性能大幅改进

DigitalOcean 希望可以为企业提供所需的工具和基础设施&#xff0c;以帮助企业客户加速云端的开发&#xff0c;实现业务的指数级增长。为此 DigitalOcean 在 2020 年就推出了App Platform。 App Platform&#xff08;应用托管&#xff09; 是一个完全托管的 PaaS 解决方案&…...

c/c++对于char*的理解(联合string容器)

在C和C中&#xff0c;char*是一个指向字符&#xff08;char&#xff09;的指针。它经常被用来处理C风格的字符串&#xff0c;这种字符串是以空字符&#xff08;\0&#xff09;结尾的字符数组。以下是关于char*的一些关键点&#xff1a; C风格的字符串&#xff1a; C风格的字符…...

Web前端三大主流框架是什么?

Web前端开发领域的三大主流框架分别是Angular、React和Vue.js。它们在Web开发领域中占据着重要的地位&#xff0c;各自拥有独特的特点和优势。 Angular Angular是一个由Google开发的前端框架&#xff0c;最初版本称为AngularJS&#xff0c;后来升级为Angular。它是一个完整的…...

一个基于servlet的MVC项目-登录验证

一、MVC的概念 MVC是Model、View、Controller的缩写&#xff0c;分别代表 Web 应用程序中的3种职责1 模型:用于存储数据以及处理用户请求的业务逻辑。 2视图:向控制器提交数据&#xff0c;显示模型中的数据。 3控制器:根据视图提出的请求&#xff0c;判断将请求和数据交给哪个…...

DDR5内存调优实战:手把手教你用MRW/MRR命令配置模式寄存器

DDR5内存调优实战&#xff1a;模式寄存器配置与信号完整性优化 当DDR5内存以6400MT/s的速率运行时&#xff0c;每个数据位的传输窗口仅有0.156纳秒。在这个比光传播50厘米所需时间还短的瞬间里&#xff0c;任何信号完整性问题都可能导致灾难性的误码。这就是为什么现代DDR5系统…...

UniApp登录注册页面实战:从零搭建到接口联调(附完整代码)

UniApp登录注册页面实战&#xff1a;从零搭建到接口联调 在移动应用开发中&#xff0c;登录注册功能是几乎所有应用的基础模块。作为开发者&#xff0c;我们不仅需要实现功能&#xff0c;还要考虑用户体验、安全性和性能优化。本文将带你从零开始构建一个完整的UniApp登录注册系…...

[解决方案]如何突破炉石传说信息不对称困境?HSTracker的实时数据融合技术

[解决方案]如何突破炉石传说信息不对称困境&#xff1f;HSTracker的实时数据融合技术 【免费下载链接】HSTracker A deck tracker and deck manager for Hearthstone on macOS 项目地址: https://gitcode.com/gh_mirrors/hs/HSTracker 在炉石传说对战中&#xff0c;信息…...

3步打造无广告音乐体验:xManager高效管理指南

3步打造无广告音乐体验&#xff1a;xManager高效管理指南 【免费下载链接】xManager Ad-Free, New Features & Freedom 项目地址: https://gitcode.com/GitHub_Trending/xm/xManager 还在为音乐应用广告弹窗烦恼&#xff1f;通勤路上想听首歌却被30秒广告打断&#…...

本地显卡不够用,实时云渲染能否替代本地工作站?

做三维设计、影视动画、建筑可视化的创作者&#xff0c;大多遇到过同样的困境&#xff1a;本地显卡算力不足&#xff0c;复杂场景卡顿严重&#xff0c;高清渲染耗时过长&#xff0c;升级硬件成本高、更新快&#xff0c;闲置时又造成浪费。面对这些问题&#xff0c;实时云渲染成…...

什么是WAF防火墙,WAF防火墙都有哪些功能

WAF都有哪些分类 WAF从形态上可分为硬件WAF、WAF防护软件和云WAF。 硬件WAF通常串行部署在Web服务器前端&#xff0c;用于检测、阻断异常流量。通过代理技术代理来自外部的流量&#xff0c;并对请求包进行解析&#xff0c;通过安全规则库的攻击规则进行匹配&#xff0c;如成功…...

重拾音乐自由:ncmdumpGUI让你的NCM文件重获新生 | 音乐格式转换与数字权利解放指南

重拾音乐自由&#xff1a;ncmdumpGUI让你的NCM文件重获新生 | 音乐格式转换与数字权利解放指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换&#xff0c;Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 当你在旅途中想…...

SiameseUniNLU效果实测:中文商品评论中‘品牌-功能-体验’三维属性情感抽取成果

SiameseUniNLU效果实测&#xff1a;中文商品评论中‘品牌-功能-体验’三维属性情感抽取成果 1. 项目背景与模型介绍 在电商平台的海量商品评论中&#xff0c;用户往往同时表达对品牌、产品功能和实际体验的多维度感受。传统的情感分析方法通常只能判断整体情感倾向&#xff0…...

FLUX.小红书极致真实V2多用户支持:Web UI增加账号隔离与生成历史权限管理

FLUX.小红书极致真实V2多用户支持&#xff1a;Web UI增加账号隔离与生成历史权限管理 1. 引言&#xff1a;从个人工具到团队协作的挑战 如果你用过之前的FLUX.小红书极致真实V2工具&#xff0c;可能会发现一个问题&#xff1a;当团队里有多个人都想用它来生成图片时&#xff…...

计算机复试学习笔记 Day44

130. 2n皇后问题问题描述给定一个n*n的棋盘&#xff0c;棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后&#xff0c;使任意的两个黑皇后都不在同一行、同一列或同一条对角线上&#xff0c;任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共…...