SpringBoot 整合WebSocket 简单实战案例

ops/2024/9/19 0:41:53/ 标签: spring boot, websocket, 后端

先是pom.xml添加依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>

PS:application.properties不需要添加任何配置 ,我只设置了一下服务server.port=8083

接着,创建节点配置类WebSocketStompConfig.java:


import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configurationpublic class WebSocketStompConfig {//这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket  ,如果你使用外置的tomcat就不需要该配置文件@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}}

然后是WebSocket配置类,WebSocket.java:

(这里面包含这单独发送消息,群发,监听上下线等等方法)


import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArraySet;import java.util.concurrent.atomic.AtomicInteger;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 com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.google.common.collect.Maps;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;/*** @Author:JCccc* @Description:* @Date: created in 15:56 2019/5/13*/@Component@ServerEndpoint(value = "/connectWebSocket/{userId}")public class WebSocket {private Logger logger = LoggerFactory.getLogger(this.getClass());/*** 在线人数*/public static int onlineNumber = 0;/*** 以用户的姓名为key,WebSocket为对象保存起来*/private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();/*** 会话*/private Session session;/*** 用户名称*/private String userId;/*** 建立连接** @param session*/@OnOpenpublic void onOpen(@PathParam("userId") String userId, Session session){onlineNumber++;logger.info("现在来连接的客户id:"+session.getId()+"用户名:"+userId);this.userId = userId;this.session = session;//  logger.info("有新连接加入! 当前在线人数" + onlineNumber);try {//messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息//先给所有人发送通知,说我上线了Map<String,Object> map1 = Maps.newHashMap();map1.put("messageType",1);map1.put("userId",userId);sendMessageAll(JSON.toJSONString(map1),userId);//把自己的信息加入到map当中去clients.put(userId, this);logger.info("有连接关闭! 当前在线人数" + clients.size());//给自己发一条消息:告诉自己现在都有谁在线Map<String,Object> map2 = Maps.newHashMap();map2.put("messageType",3);//移除掉自己Set<String> set = clients.keySet();map2.put("onlineUsers",set);sendMessageTo(JSON.toJSONString(map2),userId);}catch (IOException e){logger.info(userId+"上线的时候通知所有人发生了错误");}}@OnErrorpublic void onError(Session session, Throwable error) {logger.info("服务端发生了错误"+error.getMessage());//error.printStackTrace();}/*** 连接关闭*/@OnClosepublic void onClose(){onlineNumber--;//webSockets.remove(this);clients.remove(userId);try {//messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息Map<String,Object> map1 = Maps.newHashMap();map1.put("messageType",2);map1.put("onlineUsers",clients.keySet());map1.put("userId",userId);sendMessageAll(JSON.toJSONString(map1),userId);}catch (IOException e){logger.info(userId+"下线的时候通知所有人发生了错误");}//logger.info("有连接关闭! 当前在线人数" + onlineNumber);logger.info("有连接关闭! 当前在线人数" + clients.size());}/*** 收到客户端的消息** @param message 消息* @param session 会话*/@OnMessagepublic void onMessage(String message, Session session){try {logger.info("来自客户端消息:" + message+"客户端的id是:"+session.getId());System.out.println("------------  :"+message);JSONObject jsonObject = JSON.parseObject(message);String textMessage = jsonObject.getString("message");String fromuserId = jsonObject.getString("userId");String touserId = jsonObject.getString("to");//如果不是发给所有,那么就发给某一个人//messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息Map<String,Object> map1 = Maps.newHashMap();map1.put("messageType",4);map1.put("textMessage",textMessage);map1.put("fromuserId",fromuserId);if(touserId.equals("All")){map1.put("touserId","所有人");sendMessageAll(JSON.toJSONString(map1),fromuserId);}else{map1.put("touserId",touserId);System.out.println("开始推送消息给"+touserId);sendMessageTo(JSON.toJSONString(map1),touserId);}}catch (Exception e){e.printStackTrace();logger.info("发生了错误了");}}public void sendMessageTo(String message, String TouserId) throws IOException {for (WebSocket item : clients.values()) {//    System.out.println("在线人员名单  :"+item.userId.toString());if (item.userId.equals(TouserId) ) {item.session.getAsyncRemote().sendText(message);break;}}}public void sendMessageAll(String message,String FromuserId) throws IOException {for (WebSocket item : clients.values()) {item.session.getAsyncRemote().sendText(message);}}public static synchronized int getOnlineCount() {return onlineNumber;}}

接下来用一个HTML5 页面 index.html,连接当前的WebSocket节点,接/发消息, index.html:


<!DOCTYPE HTML><html><head><title>Test My WebSocket</title></head><body>TestWebSocket<input  id="text" type="text" /><button onclick="send()">SEND MESSAGE</button><button onclick="closeWebSocket()">CLOSE</button><div id="message"></div></body><script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif('WebSocket' in window){//连接WebSocket节点websocket = new WebSocket("ws://localhost:8083/connectWebSocket/001");}else{alert('Not support websocket')}//连接发生错误的回调方法websocket.onerror = function(){setMessageInnerHTML("error");};//连接成功建立的回调方法websocket.onopen = function(event){setMessageInnerHTML("open");}//接收到消息的回调方法websocket.onmessage = function(event){setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function(){setMessageInnerHTML("close");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function(){websocket.close();}//将消息显示在网页上function setMessageInnerHTML(innerHTML){document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭连接function closeWebSocket(){websocket.close();}//发送消息function send(){var message = document.getElementById('text').value;websocket.send(message);}</script></html>

为了演示多人接入,我们再弄一个index2.html:

好了,一切准备就绪,那么 把项目跑起来:

访问index.html模拟用户001连接websocket服务:

可以看到一上线,我们默认就推送了一条上线消息

接下来继续访问index2.html,模拟用户002也接入websocket

此刻,我们模拟咱们服务器给客户推送消息,有群发和单独发送,我们一一实践:

单独发送,只需要调用websocket.java里面的 sendMessageTo方法:

那么我们来写个简单的推送信息接口,

@AutowiredWebSocket webSocket;@ResponseBody@GetMapping("/sendTo")public String sendTo(@RequestParam("userId") String userId,@RequestParam("msg") String msg) throws IOException {webSocket.sendMessageTo(msg,userId);return "推送成功";}

http://www.ppmy.cn/ops/109368.html

相关文章

一个简单快捷的PHP日期时间助手类库

项目特点 简单易用&#xff1a;不依赖任何扩展,支持Composer,开箱即用. 化繁为简&#xff1a;所有方法都可以传入任意类型的时间日期格式或时间戳. 快捷高效&#xff1a;所有操作只需要一个静态方法即可搞定 长期维护&#xff1a;作者为自由职业者,保证项目的长期稳定和持续…

算法设计与分析(二分查找算法

目录 二分查找算法详解引言算法步骤代码实现注意事项结论 小结&#xff1a; 二分查找算法详解 引言 二分查找算法是一种在有序数组中查找特定元素的搜索算法。它通过不断将数组分成两半&#xff0c;缩小搜索范围&#xff0c;从而快速定位到目标元素的位置。二分查找算法的时间…

【案例】Jimmy Jazz借力7thonline第七在线实现商品管理智能化升级

20 余年前&#xff0c;詹姆斯∙克兹瑞开始质疑为什么美国城市的居民只能前往地处郊区的大型购物商场才能找到喜欢的品牌服装&#xff0c;这激发了他将时尚商品和高品质零售体验带入城市中心的愿景。在这个想法的激励下&#xff0c;他在纽约市德兰西街开了一家服装店&#xff0c…

【LeetCode】:面试题 16.05. 阶乘尾数

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;C课程学习 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 好久没有写文章了&#xff0c;今天碰见了一道有趣的题目&#xff0c;写下来分享一下。 &#x1f3c6;1.问题描…

Nginx跨域运行案例:云台控制http请求,通过 http server 代理转发功能,实现跨域运行。(基于大华摄像头WEB无插件开发包)

文章目录 引言I 跨域运行案例开发资源测试/生产环境,Nginx代理转发,实现跨域运行本机开发运行II nginx的location指令Nginx配置中, 获取自定义请求header头Nginx 配置中,获取URL参数引言 背景:全景监控 需求:感知站点由于云台相关操作为 http 请求,http 请求受浏览器…

设计模式 23 访问者模式

设计模式 23 创建型模式&#xff08;5&#xff09;&#xff1a;工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式结构型模式&#xff08;7&#xff09;&#xff1a;适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式行为型模式&#xff…

CentOS系统上Node.js安装与配置最佳实践

由于nvm下载node需要翻墙速度非常慢&#xff08;试过很多次都不行&#xff09;&#xff0c;所以推荐手动安装&#xff0c;步骤也很简单 1、官网下载 打开官网 Node.js官网下载 选择自己系统合适的版本下载&#xff0c;我这里下载的是Linux x64的v18.20版本 2.上传服务器 将…

vue中子组件的mounted/created钩子不会等待父组件created钩子中的异步方法执行完,生命周期不是跨组件的依赖或等待

项目实战遇到的问题&#xff1a; 打开新标签页指定路由地址&#xff0c;组件会进行重新创建挂载问题。 在A组件created中使用异步操作发起请求&#xff0c;而请求返回的数据假设id&#xff0c;存储到vuex的store中&#xff0c;在B/C组件中通过computed MapGetter获取数据id并在…

C++ IO流全解析:标准库中的数据处理与文件读写艺术

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;C从入门到精通 目录 一&#xff1a; &#x1f525; C语言的输入与输出 二&#xff1a; &#x1f525; 流是什么 三&#xff1a; &#x1f525; CIO流&#x1f680; 3.1 C标准IO流&#x1f680; ist…

ORACLE 误删表后恢复

-- 查询表删除记录 PORTAL_APP 为表名 select * from user_recyclebin where original_name PORTAL_APP order by droptime desc; --回滚最近一次的删除 FLASHBACK TABLE PORTAL_APP TO BEFORE DROP; --如果提示表已经存在则给恢复的表重命名成别的 FLASHBACK TABLE PORTAL_…

【python】socket 入门以及多线程tcp链接

Socket 入门 及 多线程tcp链接 网络基础知识三要素 Socket是套接字的意思,是网络编程的核心对象,通信两端都独有自己的Socket对象, 数据在两个Socket之间通过 字节流(TCP协议) 或者 数据报包(UDP协议)的形式进行传输. 本文主要针对tcp流程进行讲解 socket-tcp流程图 1.创建服…

基础算法(2)——滑动窗口

1. 长度最小的子数组 题目描述&#xff1a; 解法一&#xff1a;暴力枚举出所有的子数组的和&#xff08;会超时&#xff09; 算法思路&#xff1a;&#xff08;时间复杂度&#xff09; 从前往后枚举数组中的任意一个元素&#xff0c;把它当成起始位置&#xff0c;然后从这个…

k8s控制器

一、部署 已经搭好的harbor仓库 scp ca.crt root172.25.250.100 scp ca.crt root172.25.250.100:/etc/docker/certs.d/bwmis.org/ scp ca.crt root172.25.250.110:/etc/docker/certs.d/bwmis.org/ scp ca.crt root172.25.250.120:/etc/docker/certs.d/bwmis.org/ ##发送证…

Redis常见面试问题

一.redis的应用场景 缓存分布式锁Token存储短信验证码存储计数器全局唯一id排行榜限流购物车点赞关注分布式Session发布订阅延迟队列消息队列 1.分布式锁 锁&#xff0c;即在多线程环境下&#xff0c;对共享资源的访问造成的线程安全问题&#xff0c;通过锁的机制来实现资源访…

高德地图SDK Android版开发 10 InfoWindow

高德地图SDK Android版开发 10 InfoWindow 前言相关类和方法默认样式Marker类AMap类AMap.OnInfoWindowClickListener 接口 自定义样式(视图)AMap 类AMap.ImageInfoWindowAdapter 接口 自定义样式(Image)AMap.ImageInfoWindowAdapter 接口 示例界面布局MapInfoWindow类常量成员变…

java-redis-穿透

Redis 缓存穿透是指当请求的数据在缓存和数据库中都不存在时&#xff0c;用户每次请求都会直接查询数据库&#xff0c;导致缓存失效&#xff0c;无法发挥作用。这种情况下&#xff0c;用户发出的每个请求都绕过了缓存&#xff0c;直接打到了数据库&#xff0c;可能导致数据库压…

C++复习day08

一、C11 1.列表初始化 在C98中&#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如&#xff1a; struct Point {int _x;int _y; }; int main() {int array1[] { 1, 2, 3, 4, 5 };int array2[5] { 0 };Point p { 1, 2 };return 0; }C11扩…

深度学习Day-32:CycleGAN实战

&#x1f368; 本文为&#xff1a;[&#x1f517;365天深度学习训练营] 中的学习记录博客 &#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制] 一、 基础配置 语言环境&#xff1a;Python3.8编译器选择&#xff1a;Pycharm深度学习环境&#xff1a; torch1.12.1c…

多线程:java中的实现

实现1&#xff1a; 通过java.util.concurrent.atomic中的原子性数据实现 static class Counter {// 通过加锁实现同步public static int count 0;public static final Object obj new Object(); // 通过原子性的整型来实现同步public static AtomicInteger c…

如何解决分布式锁占用不释放的场景

在分布式系统中&#xff0c;处理分布式锁时&#xff0c;若一个节点获取了锁但未能及时释放&#xff0c;可能会导致其他节点无法获取锁&#xff0c;进而引发死锁或资源长时间占用的情况。为了解决这一问题&#xff0c;以下几种方法可以有效应对&#xff0c;具体措施描述如下&…