当前位置: 首页 > 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;判断将请求和数据交给哪个…...

嵌入式Linux驱动开发核心技术解析

嵌入式Linux驱动工程师面试技术要点解析1. Linux驱动开发核心技术考察1.1 进程同步机制Linux内核提供了多种进程同步机制&#xff0c;包括&#xff1a;信号量&#xff08;Semaphore&#xff09;&#xff1a;用于控制对共享资源的访问互斥锁&#xff08;Mutex&#xff09;&#…...

6大终极方案!WarcraftHelper全方位解决魔兽争霸III在Win10/11兼容性难题

6大终极方案&#xff01;WarcraftHelper全方位解决魔兽争霸III在Win10/11兼容性难题 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 经典游戏魔兽争霸…...

系统架构设计师知识点21-40

21.ABSD方法的三个基础。①功能分解&#xff0c;使用已有的基于模块的内聚与耦合技术②选择架构风格实现质量和业务需求③软件模板使用22.ABSD方法是一个自顶向下&#xff0c;递归细化的方法&#xff0c;软件系统的体系结构通过该方法得到细化&#xff0c;直到能产生软件构件和…...

实战演练:基于快马平台快速构建一个电商场景的智能客服AI Agent

实战演练&#xff1a;基于快马平台快速构建一个电商场景的智能客服AI Agent 最近在做一个电商项目&#xff0c;需要给平台增加智能客服功能。传统开发流程要写大量业务逻辑代码&#xff0c;还要处理前后端对接&#xff0c;想想就头大。后来发现用InsCode(快马)平台可以快速实现…...

Midjourney 图像到图像转换:真实人物与动漫的一致性与多样场景选择

Midjourney 拥有强大的图像到图像转换能力。本文将手把手教你如何在我们的 AceDataCloud 网站 上将照片切换到任何动漫场景&#xff0c;同时保持角色的一致性。 通过以下步骤&#xff0c;我们可以轻松实现角色一致性。 接下来&#xff0c;我们看一下效果&#xff0c;原始图像如…...

Apache Sedona 使用教程

Apache Sedona 使用教程 项目介绍 Apache Sedona 是一个用于大规模空间数据处理的分布式计算系统。它基于 Apache Spark&#xff0c;提供了高效的空间数据处理能力&#xff0c;支持多种空间数据类型和操作。Sedona 旨在为大数据环境下的地理空间分析提供强大的支持&#xff0c;…...

快充、便携、安全兼备,Anker能量盒到底香不香?

随着无线互联网时代的到来&#xff0c;移动设备的续航问题成为人们的新烦恼。无论是频繁出差、旅行&#xff0c;还是移动办公&#xff0c;充电宝几乎已经成为随身必备的装备。 然而&#xff0c;传统充电宝往往存在充电速度慢、体积笨重、功能单一&#xff0c;甚至安全认证不完善…...

BERT-base-uncased完全指南:从基础原理到实战应用

BERT-base-uncased完全指南&#xff1a;从基础原理到实战应用 【免费下载链接】bert-base-uncased 项目地址: https://ai.gitcode.com/hf_mirrors/ai-gitcode/bert-base-uncased 一、认知铺垫&#xff1a;为什么BERT改变了NLP格局&#xff1f; 1.1 BERT的突破性意义何…...

MinerU本地部署安全吗?数据隐私保护实战配置

MinerU本地部署安全吗&#xff1f;数据隐私保护实战配置 1. 引言&#xff1a;当AI遇见你的敏感文档 想象一下这个场景&#xff1a;你有一份包含商业机密的合同PDF&#xff0c;或者一份涉及个人隐私的医疗报告扫描件。你想用AI快速提取里面的关键信息&#xff0c;但又担心把文…...

新手零基础入门:借助快马AI生成你的第一个班级宠物园网页应用

作为一个刚接触编程的新手&#xff0c;想要快速上手开发一个班级宠物园网页应用&#xff0c;确实会遇到不少挑战。不过现在有了InsCode(快马)平台这样的工具&#xff0c;整个过程变得简单多了。下面我就分享一下自己从零开始构建这个项目的经验&#xff0c;希望能帮助到同样想入…...