SpringBoot集成WebSocket实现消息实时推送(提供Gitee源码)

news/2024/11/8 16:41:42/

前言:在最近的工作当中,客户反应需要实时接收消息提醒,这个功能虽然不大,但不过也用到了一些新的技术,于是我这边写一个关于我如何实现这个功能、编写、测试到部署服务器,归纳到这篇博客中进行总结。

目录

一、什么是WebSocket

二、后端实现

1、引入pom.xml依赖

2、注册WebSocket核心配置类

3、编写WebSocket服务器核心代码

三、前端实现

四、本地测试

五、打包前后端

六、部署到服务器测试

七、Gitee源码


一、什么是WebSocket

WebSocket是一种建立在TCP连接上的全双工通信协议,让客户端和服务端之间可以做到实时通信,允许服务端和客户端互相推送数据,服务端和客户端只要建立一次握手,两者之间便可以创建持久连接实现数据的双向传输。

二、后端实现

1、引入pom.xml依赖

        <!-- 集成websocket实现实时通信 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- 常用工具类 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version></dependency>

2、注册WebSocket核心配置类

package com.example.websocket.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig
{@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}
}

3、编写WebSocket服务器核心代码

package com.ithuang.websocket.config;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.concurrent.ConcurrentHashMap;@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketServer {/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/private static int onlineCount = 0;/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/**接收userId*/private String userId="";/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {this.session = session;this.userId=userId;if(webSocketMap.containsKey(userId)){webSocketMap.remove(userId);webSocketMap.put(userId,this);//加入set中}else{webSocketMap.put(userId,this);//加入set中addOnlineCount();//在线数加1}log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());try {sendMessage("连接成功");} catch (IOException e) {log.error("用户:"+userId+",网络异常!!!!!!");}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {if(webSocketMap.containsKey(userId)){webSocketMap.remove(userId);//从set中删除subOnlineCount();}log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) {log.info("用户消息:"+userId+",报文:"+message);//可以群发消息//消息保存到数据库、redis
//        if(StringUtils.isNotBlank(message)){
//            try {
//                //解析发送的报文
//                JSONObject jsonObject = JSON.parseObject(message);
//
//            }catch (Exception e){
//                e.printStackTrace();
//            }
//        }}/**** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误:"+this.userId+",原因:"+error.getMessage());}/*** 实现服务器主动推送*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);}/*** 实现服务器主动推送*/public void sendAllMessage(String message) throws IOException {ConcurrentHashMap.KeySetView<String, WebSocketServer> userIds = webSocketMap.keySet();for (String userId : userIds) {WebSocketServer webSocketServer = webSocketMap.get(userId);webSocketServer.session.getBasicRemote().sendText(message);}}/*** 发送自定义消息* */public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {log.info("发送消息到:"+userId+",报文:"+message);if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){webSocketMap.get(userId).sendMessage(message);}else{log.error("用户"+userId+",不在线!");}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketServer.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketServer.onlineCount--;}
}

4、撰写定时任务

注:主启动类需要加上@EnableScheduling,否则不生效。

package com.ithuang.websocket.task;import com.ithuang.websocket.config.WebSocketServer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;@Component
public class Send {@Resourceprivate WebSocketServer webSocketServer;@Scheduled(fixedDelay = 2000)public void sendMsg() throws IOException {webSocketServer.sendAllMessage("hello"+new Date());}
}

三、前端实现

需要先自行安装好element-ui组件库,这边不做演示,直接贴出代码

<template><div><template><div><el-input v-model="url" type="text" style="width: 20%" /> &nbsp; &nbsp;<el-button @click="initWebsocket" type="primary">连接</el-button><el-button @click="exit" type="danger">断开</el-button><br /><el-input type="textarea" v-model="message" :rows="9" /><el-button type="info" @click="send">发送消息</el-button><br /><br /><el-input type="textarea" v-model="text_content" :rows="9" /> 返回内容<br /><br /></div></template></div>
</template><script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'export default {name: 'HomeView',components: {HelloWorld},data() {return {url: "ws://127.0.0.1:9090/websocket/1500",message: "",text_content: "",ws: null,};},created() {},destroyed() {this.exit()},methods:{initWebsocket(){this.ws = new WebSocket(this.url);const self = this;this.ws.onopen = function (event) {// self.text_content = self.text_content + "已经打开连接!" + "\n";};this.ws.onmessage = function (event) {self.text_content = self.text_content + event.data +"\n";};this.ws.onclose = function (event) {// self.text_content = self.text_content + "已经关闭连接!" + "\n";};},exit() {if (this.ws) {this.ws.close();this.ws = null;}},send() {if (this.ws) {this.ws.send(this.message);} else {alert("未连接到服务器");}},}
}
</script>

四、本地测试

点击连接

当点击断开或者关闭浏览器,后台输出如下

五、打包前后端

具体的打包方式这边不再做阐述,在后端打包可能会出现如下异常:

解决如下:

在单元测试类上面加上此注解:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

六、部署到服务器测试

具体的部署流程这边不再贴出,把默认的127.0.0.1替换成自己的服务器IP,点击连接即可,后端就会每2秒推送一次消息了,看代码结果。

部署的前端端口默认是80,后端端口是9090,记得放行端口。

后台日志如下: 

七、Gitee源码

前端代码:vue-websocket: vue实现websocket连接的前端代码

后端代码:springboot-websocket: springboot后端实现websocket连接源码 

 


http://www.ppmy.cn/news/514798.html

相关文章

云计算成本大揭秘:硬件、研发、电力等各项成本都在这里!

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 云计算作为一种技术和商业模式&#xff0c;已经深深地影响了全球的IT行业和各种商业运营。云服务商的主要模式以订阅为基础&#xff0c;一旦应用程序和工作负载移动到云上&#xff0c;它们通常会停留在那里&#xff0c;订阅…

【Python 基础篇】Python 文件操作

文章目录 导言一、文件操作的作用二、文件的基本操作1、打开文件2、读写文件① 读取文件② 写入文件 3、关闭文件 三、文件备份四、文件和文件夹的操作结语 导言 在编程领域中&#xff0c;文件操作是一项基础且常见的任务。无论是读取配置文件、处理数据文件&#xff0c;还是备…

【python】如何在 Python 中创建相关矩阵

目录 一、说明 二、相关理论 2.1 何为相关 2.2 相关的前提 2.3 Correlation Matrix是个啥&#xff1f; 2.4 皮尔逊相关系数 三、Python演示如何创建相关矩阵 四、数据可视化观察 五、后记 一、说明 本教程介绍如何在 Python 中创建和解释相关矩阵。然而&#xff0c;创…

M78

首先我们发现了一个漏洞 这里读入0x199大小的buf&#xff0c;而下面的check函把buf的内容付给dest&#xff0c;而dest大小显然溢出了 但是我们发现这个题目有字符串长度限制必须等于7&#xff0c;我们要溢出至少需要34个字节&#xff0c;这就是这题的主要难点 strlen是一个统计…

与吉凯恩GKN建立EDI连接需要掌握哪些信息?

项目背景 GKN集团&#xff08;吉凯恩集团&#xff09;创建于1759年&#xff0c;已有260年的历史。主要业务有大型民航客机和运输机结构件&#xff0c;汽车传动系统&#xff0c;非高速公路用工作车辆和特种车辆&#xff0c;农用机械&#xff0c;粉末冶金&#xff0c;新型合金粉…

伊士曼将在法国建立分子塑料回收设施;吉凯恩粉末冶金公司任命新任首席执行官 | 能动...

石油和化工 Celgard和Farasis达成和解。Polypore International, LP旗下子公司Celgard, LLC(以下简称“Celgard”)就其在美国加利福尼亚州北区联邦地区法院(NDCA)针对多个Farasis被告提起的专利诉讼成功达成和解。Celgard及此项诉讼中列出的所有Farasis实体已就Celgard和Farasi…

凯恩斯显灵啦!

昨天到超市买东西&#xff0c;竟然花了1000块&#xff0c;感觉跟以前花300块买到的东西差不多&#xff0c;难道物价真的涨了那么多&#xff1f;排队结帐&#xff0c;看到账单的时候&#xff0c;吓了一跳&#xff0c;刹那间&#xff0c;想到了凯恩斯的一句话&#xff1a; 通过连…

PASCAL语言创始人:尼克劳斯.威茨

来自&#xff1a;CSDN 学过计算机的人大都知道“算法数据结构程序”这一著名公式&#xff0c;提出该公式的正是1984年的图灵奖获得者&#xff0c;瑞士计算机科学家尼克劳斯•威茨(Niklaus Wirth)。到目前为止&#xff0c;他是获得图灵奖殊荣的惟一瑞士学者。 威茨于1934年2月…