SpringBoot2.x 整合websocket 消息推送,单独发送信息,群发信息

根据公司需求在SpringBoot项目中集成站内信,于是,我做了一个SpringBoot2.x 整合websocket 消息推送,给指定用户发送信息和群发信息即点点对方式和广播方式2种模式。

在这里插入图片描述
在这里插入图片描述

文章目录

  • 一、地址部署总览
  • 二、实战需求案例
  • 三、实战准备
    • 3.1. pom依赖
    • 3.2. application.yml
    • 3.3. 配置类
    • 3.4. 实体类
    • 3.5. websocket 服务端
    • 3.6. 控制器
    • 3.7. SpringBoot入口类
  • 四、初始化页面总览
    • 4.1. 服务端
    • 4.2. 客户端A
    • 4.3. 客户端B
    • 4.4. 客户端C
  • 五、案例实战
    • 5.1. 客户端A连接服务端
    • 5.2. 客户端B连接服务端
    • 5.3. 客户端C连接服务端
    • 5.4. 服务端连接状态ABC
  • 六、单独发送信息
    • 6.1. 服务端给指定客户端A发送消息
    • 6.2. 验证客户端A消息是否收到
  • 七、群发信息
    • 7.1. 给在线客户端群发消息
    • 7.2. 客户端A 消息验证
    • 7.2. 客户端B 消息验证
    • 7.3. 客户端C 消息验证

一、地址部署总览

服务端地址http://localhost:8086/admin
客户端地址http://localhost:8086/index

二、实战需求案例

服务端实例1个
客户端A实例1
客户端B实例2
客户端C实例3

三、实战准备

3.1. pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.gblfy</groupId><artifactId>springboot-websocket</artifactId><version>v1.0.0</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.1.RELEASE</version></parent><properties><!--编码同意设置--><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!--JDK版本--><java.version>1.8</java.version></properties><dependencies><!--SpringMVC启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--热部署插件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><!--websocket启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!--thymeleaf 模板引擎启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency></dependencies><build><plugins><!--maven 打包编译插件--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

3.2. application.yml

server:port: 80
spring:devtools:restart:exclude:  static/**,public/**enabled:  true

3.3. 配置类

package com.gblfy.websocket.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** 开启websocket的支持** @Author gblfy* @Email gbly02@gmail.com* @Date 2019/11/20 PM 23:50*/
@Configuration  
public class WebSocketConfig {  @Bean  public ServerEndpointExporter serverEndpointExporter(){  return new ServerEndpointExporter();  }  
}  

3.4. 实体类

package com.gblfy.websocket.entity;import javax.websocket.Session;
import java.io.Serializable;/*** @Author gblfy* @Email gbly02@gmail.com* @Date 2019/11/20 PM 23:50*/
public class Client implements Serializable {private static final long serialVersionUID = 8957107006902627635L;private String userName;private Session session;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Session getSession() {return session;}public void setSession(Session session) {this.session = session;}public Client(String userName, Session session) {this.userName = userName;this.session = session;}public Client() {}
}

3.5. websocket 服务端

package com.gblfy.websocket.server;import com.gblfy.websocket.entity.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;/**** @Author gblfy* @Email gbly02@gmail.com* @Date 2019/11/20 PM 23:50*/
@ServerEndpoint(value = "/socketServer/{userName}")
@Component
public class SocketServer {private static final Logger logger = LoggerFactory.getLogger(SocketServer.class);/**** 用线程安全的CopyOnWriteArraySet来存放客户端连接的信息*/private static CopyOnWriteArraySet<Client> socketServers = new CopyOnWriteArraySet<>();/**** websocket封装的session,信息推送,就是通过它来信息推送*/private Session session;/**** 服务端的userName,因为用的是set,每个客户端的username必须不一样,否则会被覆盖。* 要想完成ui界面聊天的功能,服务端也需要作为客户端来接收后台推送用户发送的信息*/private final static String SYS_USERNAME = "niezhiliang9595";/**** 用户连接时触发,我们将其添加到* 保存客户端连接信息的socketServers中** @param session* @param userName*/@OnOpenpublic void open(Session session,@PathParam(value="userName")String userName){this.session = session;socketServers.add(new Client(userName,session));logger.info("客户端:【{}】连接成功",userName);}/**** 收到客户端发送信息时触发* 我们将其推送给客户端(niezhiliang9595)* 其实也就是服务端本身,为了达到前端聊天效果才这么做的** @param message*/@OnMessagepublic void onMessage(String message){Client client = socketServers.stream().filter( cli -> cli.getSession() == session).collect(Collectors.toList()).get(0);sendMessage(client.getUserName()+"<--"+message,SYS_USERNAME);logger.info("客户端:【{}】发送信息:{}",client.getUserName(),message);}/**** 连接关闭触发,通过sessionId来移除* socketServers中客户端连接信息*/@OnClosepublic void onClose(){socketServers.forEach(client ->{if (client.getSession().getId().equals(session.getId())) {logger.info("客户端:【{}】断开连接",client.getUserName());socketServers.remove(client);}});}/**** 发生错误时触发* @param error*/@OnErrorpublic void onError(Throwable error) {socketServers.forEach(client ->{if (client.getSession().getId().equals(session.getId())) {socketServers.remove(client);logger.error("客户端:【{}】发生异常",client.getUserName());error.printStackTrace();}});}/**** 信息发送的方法,通过客户端的userName* 拿到其对应的session,调用信息推送的方法* @param message* @param userName*/public synchronized static void sendMessage(String message,String userName) {socketServers.forEach(client ->{if (userName.equals(client.getUserName())) {try {client.getSession().getBasicRemote().sendText(message);logger.info("服务端推送给客户端 :【{}】",client.getUserName(),message);} catch (IOException e) {e.printStackTrace();}}});}/**** 获取服务端当前客户端的连接数量,* 因为服务端本身也作为客户端接受信息,* 所以连接总数还要减去服务端* 本身的一个连接数** 这里运用三元运算符是因为客户端第一次在加载的时候* 客户端本身也没有进行连接,-1 就会出现总数为-1的情况,* 这里主要就是为了避免出现连接数为-1的情况** @return*/public synchronized static int getOnlineNum(){return socketServers.stream().filter(client -> !client.getUserName().equals(SYS_USERNAME)).collect(Collectors.toList()).size();}/**** 获取在线用户名,前端界面需要用到* @return*/public synchronized static List<String> getOnlineUsers(){List<String> onlineUsers = socketServers.stream().filter(client -> !client.getUserName().equals(SYS_USERNAME)).map(client -> client.getUserName()).collect(Collectors.toList());return onlineUsers;}/**** 信息群发,我们要排除服务端自己不接收到推送信息* 所以我们在发送的时候将服务端排除掉* @param message*/public synchronized static void sendAll(String message) {//群发,不能发送给服务端自己socketServers.stream().filter(cli -> cli.getUserName() != SYS_USERNAME).forEach(client -> {try {client.getSession().getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}});logger.info("服务端推送给所有客户端 :【{}】",message);}/**** 多个人发送给指定的几个用户* @param message* @param persons*/public synchronized static void SendMany(String message,String [] persons) {for (String userName : persons) {sendMessage(message,userName);}}
}

3.6. 控制器

package com.gblfy.websocket.controller;import com.gblfy.websocket.server.SocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;/*** websocket* 消息推送(个人和广播)** @Author gblfy* @Email gbly02@gmail.com* @Date 2019/11/20 PM 23:50*/
@Controller
public class WebSocketController {@Autowiredprivate SocketServer socketServer;/**** 客户端页面* @return*/@RequestMapping(value = "/index")public String idnex() {return "index";}/**** 服务端页面* @param model* @return*/@RequestMapping(value = "/admin")public String admin(Model model) {int num = socketServer.getOnlineNum();List<String> list = socketServer.getOnlineUsers();model.addAttribute("num",num);model.addAttribute("users",list);return "admin";}/*** 个人信息推送* @return*/@RequestMapping("sendmsg")@ResponseBodypublic String sendmsg(String msg, String username){//第一个参数 :msg 发送的信息内容//第二个参数为用户长连接传的用户人数String [] persons = username.split(",");SocketServer.SendMany(msg,persons);return "success";}/*** 推送给所有在线用户* @return*/@RequestMapping("sendAll")@ResponseBodypublic String sendAll(String msg){SocketServer.sendAll(msg);return "success";}
}

3.7. SpringBoot入口类

package com.gblfy.websocket;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** SpringBoot 启动器入口*/
@SpringBootApplication
public class SpringBootWebSocketApplication {public static void main(String[] args) {SpringApplication.run(SpringBootWebSocketApplication.class,args);}
}

四、初始化页面总览

4.1. 服务端

在这里插入图片描述

4.2. 客户端A

在这里插入图片描述

4.3. 客户端B

在这里插入图片描述

4.4. 客户端C

在这里插入图片描述

五、案例实战

5.1. 客户端A连接服务端

在这里插入图片描述

5.2. 客户端B连接服务端

在这里插入图片描述

5.3. 客户端C连接服务端

在这里插入图片描述

5.4. 服务端连接状态ABC

在这里插入图片描述

六、单独发送信息

6.1. 服务端给指定客户端A发送消息

在这里插入图片描述

6.2. 验证客户端A消息是否收到

在这里插入图片描述

七、群发信息

7.1. 给在线客户端群发消息

在这里插入图片描述

7.2. 客户端A 消息验证

在这里插入图片描述

7.2. 客户端B 消息验证

在这里插入图片描述

7.3. 客户端C 消息验证

在这里插入图片描述
从以上图中可以看出,测试符合预期。

项目源码:https://github.com/gb-heima/springboot-websocket

zip包下载链接:
https://github.com/gb-heima/springboot-websocket/archive/master.zip

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/522716.shtml

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

相关文章

Spark精华问答 | 为什么要学Spark?

戳蓝字“CSDN云计算”关注我们哦&#xff01;为什么要学习Spark&#xff1f;作为一个用来实现快速而通用的集群计算的平台。扩展广泛使用的MapReduce计算模型&#xff0c;而且高效地支持更多的计算模式&#xff0c;包括交互式查询和流处理。Spark的一个重要特点就是能够在内存中…

oracle导入dmp清除之前,oracle导入dmp遇到的有关问题

oracle导入dmp遇到的问题一、 首先要明白&#xff0c;导入导出dmp文件是通过cmd命令执行的&#xff0c;而不是通过SQL plus执行的.此外也可以借助PLSQLDev工具进行导入导出记得“导出可执行文件”选择客户端安装好的bin下的exp.exe工具二、发现在导出的过程有问题&#xff1a;网…

每个人都应该知道的25个大数据术语

摘要&#xff1a; 如果你初来乍到&#xff0c;大数据看起来很吓人!根据你掌握的基本理论&#xff0c;让我们专注于一些关键术语以此给你的约会对象、老板、家人或者任何一个人带来深刻的印象。 让我们开始吧&#xff1a; 1.算法。“算法”如何与大数据相关?即使算法是一个通用…

【个推CTO谈数据智能】之本质及技术体系要求

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者|安森来源|个推技术学院安森&#xff0c;个推CTO毕业于浙江大学&#xff0c;现全面负责个推技术选型、研发创新、运维管理等工作&#xff0c;已带领团队开发出针对移动互联网、金融风控等行业的多项前沿数据智能解决方案。曾任M…

企业打开Redis的正确方式,来自阿里云云数据库团队的解读

摘要&#xff1a; Redis是开源的基于内存且可以持久化的分布式 Key – Value数据库。自2009年发布最初版本以来&#xff0c;Redis的热度只增不减&#xff0c;除了经常位居DB-Engines的最受欢迎Key-Value数据库榜首之外&#xff0c;看阿里云技术总监为您深度解读云数据库Redis。…

2019 年度程序员吸金榜:你排第几?

作为全球知名招聘求职网站Indeed&#xff0c;最近发布了2019年度最佳工作榜单&#xff0c;公布了2019年的行业领域及工作岗位薪酬&#xff0c;在全行业的榜单中&#xff0c;跟程序员相关的岗位有9个&#xff01;对此&#xff0c;大家纷纷留言表示程序员或成最大赢家&#xff01…

YAFFS2移植到AliOS Things指南

摘要&#xff1a; YAFFS2介绍 YAFFS&#xff08;Yet Another Flash File System&#xff09;是第一个专门为NAND Flash存储器设计的嵌入式文件系统&#xff0c;适用于大容量的存储设备。YAFFS 是基于日志的文件系统&#xff0c;提供磨损平衡和掉电恢复的健壮性。 点此查看原文&…

阿里云十年,从去“IOE”到引领云原生浪潮

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 孙浩峰出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;2008年&#xff0c;王坚从微软亚洲技术研究院空降阿里&#xff0c;担任首席架构师。这个在阿里内部尊称为“博士”的首席架构师所学却是一个心理学博…

linux下mtr命令,如何使用Linux mtr命令

对于经常和Linux打交道的技术人员来说&#xff0c;mtr命令是一个非常实用的Linux命令。它可以进行实时网络诊断。本篇文章分享了linux mtr命令的详细使用示例。以下描述来自mtr命令的软件包信息。Mtr是一种网络诊断工具&#xff0c;将ping和traceroute组合到一个程序中。Mtr提供…

IntelliJ IDEA 单行注释调整

部分人并不习惯这种风格&#xff0c;所以一般会设置为单行注释的两个斜杠跟随在代码的头部&#xff0c;在File -> Setting -> Editor -> Code Style -> Java -> Code Generation 下如图设置即可

高速通道-冗余物理专线接入-健康检查配置

摘要&#xff1a; 组建混合云&#xff0c;用户的IDC需要通过物理专线与阿里云VPC互通&#xff0c;多线冗余是基本配置需求。阿里云提供的冗余冗余切换方案是在VPC中&#xff0c;用户掌握切换原理&#xff0c;即可自主在控制台完成冗余负载配置。 一 VPC健康检查IP机制1 健康检查…

OpenStack精华问答 | OpenStack的目标是什么?

关于OpenStack的争议&#xff0c;从未停止&#xff0c;每每关于它的消息&#xff0c;都会一石激起千层浪。今天就让我们看看关于OpenStack的问答吧。1Q : 什么是OpenStack&#xff1f;A : OpenStack&#xff0c;是目前最为流行的开源云操作系统框架。深入理解OpenStack需要围绕…

linux创建定时任务命令,linux设置定时任务的方法步骤

一&#xff0c;首先登录二&#xff0c;找到文件夹三&#xff0c;查看定时任务crontab -l四&#xff0c;vi root 编辑定时任务 编辑完成后&#xff0c;点ESC&#xff0c;然后:wq时间格式分钟 小时 日期 月份 周 命令数字范围 0-59 0-23 1-31 1-12 0-7 echo "hello" &g…

idea terminal终端修改为git bash设置

idea terminal终端修改为git bash设置&#xff1a; 修改前&#xff1a; 修改后&#xff1a;

iOS KVO crash 自修复技术实现与原理解析

摘要&#xff1a; 【前言】KVO API设计非常不合理&#xff0c;于是有很多的KVO三方库&#xff0c;比如 KVOController 用更优的API来规避这些crash&#xff0c;但是侵入性比较大&#xff0c;必须编码规范来约束所有人都要使用该方式。有没有什么更优雅&#xff0c;无感知的接入…

数据中心网络架构的问题与演进 — 传统路由交换技术与三层网络架构

戳蓝字“CSDN云计算”关注我们哦&#xff01;文章目录目录传统路由交换技术路由和交换交换技术传统的 2 层交换技术具有路由功能的 3 层交换技术具有网络服务功能的 7 层交换技术路由技术三层网络架构核心层&#xff08;Core Layer&#xff09;汇聚层&#xff08;Aggregation L…

10分钟上线 - 利用函数计算构建微信小程序的Server端

摘要&#xff1a; 阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算&#xff0c;您无需管理服务器等基础设施&#xff0c;只需编写代码并上传。微信小程序是一种不需要下载安装即可使用的应用&#xff0c;它可以在微信内被便捷地获取和传播。 当微信小程序遇见serv…

'cross-env' 不是内部或外部命令,也不是可运行的程序

解决方案&#xff1a; 运行: cnpm i cross-env --save-dev

监控linux内存,linux 监控系统资源-内存

监控内存使用量&#xff1a;思路:使用free -m 提取相关数据&#xff0c;算出使用内存量输入到文件&#xff0c;并且每一次计算都与当前文件中的数据对比&#xff0c;大于文件中的数据则替换。保留当天最大内存使用量&#xff0c;每天发送邮件(可以单独写个发邮件的脚本)。脚本可…

AI+DevOps正当时

戳蓝字“CSDN云计算”关注我们哦&#xff01;随着业务复杂化和人员的增加&#xff0c;开发人员和运维人员逐渐演化成两个独立的部门&#xff0c;他们工作地点分离&#xff0c;工具链不同&#xff0c;业务目标也有差异&#xff0c;这使得他们之间出现一条鸿沟。而发布软件就是将…