Spring WebSocket实现实时通信的详细教程

简介

WebSocket 是基于TCP/IP协议,独立于HTTP协议的通信协议。WebSocket 连接允许客户端和服务器之间的全双工通信,以便任何一方都可以通过已建立的连接将数据推送到另一方。

我们常用的HTTP是客户端通过「请求-响应」的方式与服务器建立通信的,必须是客户端主动触发的行为,服务端只是做好接口被动等待请求。而在某些场景下的动作,是需要服务端主动触发的,比如向客户端发送消息、实时通讯、远程控制等。客户端是不知道这些动作几时触发的,假如用HTTP的方式,那么设备端需要不断轮询服务端,这样的方式对服务器压力太大,同时产生很多无效请求,且具有延迟性。于是才采用可以建立双向通讯的长连接协议。通过握手建立连接后,服务端可以实时发送数据与指令到设备端,服务器压力小。

Spring WebSocket是Spring框架的一部分,提供了在Web应用程序中实现实时双向通信的能力。本教程将引导你通过一个简单的例子,演示如何使用Spring WebSocket建立一个实时通信应用。

准备工作

确保你的项目中已经引入了Spring框架的WebSocket模块。你可以通过Maven添加以下依赖:

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

创建WebSocket配置类(实现WebSocketConfigurer接口)

首先,创建一个配置类,用于配置WebSocket的相关设置。

package com.ci.erp.human.config;import com.ci.erp.human.handler.WebSocketHandler;
import com.ci.erp.human.interceptor.WebSocketHandleInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;/**** Websocket配置类** @author lucky_fd* @since 2024-01-17*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 注册websocket处理器和拦截器registry.addHandler(webSocketHandler(), "/websocket/server").addInterceptors(webSocketHandleInterceptor()).setAllowedOrigins("*");registry.addHandler(webSocketHandler(), "/sockjs/server").setAllowedOrigins("*").addInterceptors(webSocketHandleInterceptor()).withSockJS();}@Beanpublic WebSocketHandler webSocketHandler() {return new WebSocketHandler();}@Beanpublic WebSocketHandleInterceptor webSocketHandleInterceptor() {return new WebSocketHandleInterceptor();}
}

上面的配置类使用@EnableWebSocket注解启用WebSocket,并通过registerWebSocketHandlers方法注册WebSocket处理器。

  • registerWebSocketHandlers:这个方法是向spring容器注册一个handler处理器及对应映射地址,可以理解成MVC的Handler(控制器方法),websocket客户端通过请求的url查找处理器进行处理

  • addInterceptors:拦截器,当建立websocket连接的时候,我们可以通过继承spring的HttpSessionHandshakeInterceptor来做一些事情。

  • setAllowedOrigins:跨域设置,*表示所有域名都可以,不限制, 域包括ip:port, 指定*可以是任意的域名,不加的话默认localhost+本服务端口

  • withSockJS: 这个是应对浏览器不支持websocket协议的时候降级为轮询的处理。

创建WebSocket消息处理器(实现TextWebSocketHandler 接口)

接下来,创建一个消息处理器,处理客户端发送的消息。

package com.ci.erp.human.handler;import cn.hutool.core.util.ObjectUtil;
import com.ci.erp.common.core.utils.JsonUtils;
import com.ci.erp.human.domain.thirdVo.YYHeartbeat;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/**** websocket处理类* 实现WebSocketHandler接口** - websocket建立连接后执行afterConnectionEstablished回调接口* - websocket关闭连接后执行afterConnectionClosed回调接口* - websocket接收客户端消息执行handleTextMessage接口* - websocket传输异常时执行handleTransportError接口** @author lucky_fd* @since 2024-01-17*/public class WebSocketHandler extends TextWebSocketHandler {/*** 存储websocket客户端连接* */private static final Map<String, WebSocketSession> connections = new HashMap<>();/*** 建立连接后触发* */@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.println("成功建立websocket连接");// 建立连接后将连接以键值对方式存储,便于后期向客户端发送消息// 以客户端连接的唯一标识为key,可以通过客户端发送唯一标识connections.put(session.getRemoteAddress().getHostName(), session);System.out.println("当前客户端连接数:" + connections.size());}/*** 接收消息* */@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {System.out.println("收到消息: " + message.getPayload());// 收到客户端请求消息后进行相应业务处理,返回结果this.sendMessage(session.getRemoteAddress().getHostName(),new TextMessage("收到消息: " + message.getPayload()));}/*** 传输异常处理* */@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {super.handleTransportError(session, exception);}/*** 关闭连接时触发* */@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {System.out.println("触发关闭websocket连接");// 移除连接connections.remove(session.getRemoteAddress().getHostName());}@Overridepublic boolean supportsPartialMessages() {return super.supportsPartialMessages();}/*** 向连接的客户端发送消息** @author lucky_fd* @param clientId 客户端标识* @param message 消息体**/public void sendMessage(String clientId, TextMessage message) {for (String client : connections.keySet()) {if (client.equals(clientId)) {try {WebSocketSession session = connections.get(client);// 判断连接是否正常if (session.isOpen()) {session.sendMessage(message);}} catch (IOException e) {System.out.println(e.getMessage());}break;}}}
}

通过消息处理器,在开发中我们就可以实现向指定客户端或所有客户端发送消息,实现相应业务功能。

创建拦截器

拦截器会在握手时触发,可以用来进行权限验证

package com.ci.erp.human.interceptor;import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import java.util.Map;/**** Websocket拦截器类** @author lucky_fd* @since 2024-01-17*/public class WebSocketHandleInterceptor extends HttpSessionHandshakeInterceptor {@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {System.out.println("拦截器前置触发");return super.beforeHandshake(request, response, wsHandler, attributes);}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {System.out.println("拦截器后置触发");super.afterHandshake(request, response, wsHandler, ex);}
}

创建前端页面客户端

最后,创建一个简单的HTML页面,用于接收用户输入并显示实时聊天信息。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Spring WebSocket Chat</title><script src="https://code.jquery.com/jquery-3.6.4.min.js"></script><script src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script>
</head>
<body>请输入:<input type="text" id="message" placeholder="Type your message">
<button onclick="sendMessage()">Send</button>
<button onclick="websocketClose()">关闭连接</button>
<div id="chat"></div><script>var socket = null;if ('WebSocket' in window) {// 后端服务port为22900socket = new WebSocket("ws://localhost:22900/websocket/server");} else if ('MozWebSocket' in window) {socket = new MozWebSocket("ws://localhost:22900/websocket/server");} else {socket = new SockJS("http://localhost:22900/sockjs/server");}// 接收消息触发socket.onmessage = function (event) {showMessage(event.data);};// 创建连接触发socket.onopen = function (event) {console.log(event.type);};// 连接异常触发socket.onerror = function (event) {console.log(event)};// 关闭连接触发socket.onclose = function (closeEvent) {console.log(closeEvent.reason);};//发送消息function sendMessage() {if (socket.readyState === socket.OPEN) {var message = document.getElementById('message').value;socket.send(message);console.log("发送成功!");} else {console.log("连接失败!");}}function showMessage(message) {document.getElementById('chat').innerHTML += '<p>' + message + '</p>';}function websocketClose() {socket.close();console.log("连接关闭");}window.close = function () {socket.onclose();};</script></body>
</html>

这个页面使用了WebSocket对象来建立连接,并通过onmessage监听收到的消息。通过输入框发送消息,将会在页面上显示。

测试结果:

后端日志:

在这里插入图片描述

前端界面:

在这里插入图片描述

Java客户端

添加依赖

<dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.4.0</version>
</dependency>

创建客户端类(继承WebsocketClient)

package com.river.websocket;import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;import java.net.URI;
import java.net.URISyntaxException;public class MyWebSocketClient extends WebSocketClient {MyWebSocketClient(String url) throws URISyntaxException {super(new URI(url));}// 建立连接@Overridepublic void onOpen(ServerHandshake shake) {System.out.println(shake.getHttpStatusMessage());}// 接收消息@Overridepublic void onMessage(String paramString) {System.out.println(paramString);}// 关闭连接@Overridepublic void onClose(int paramInt, String paramString, boolean paramBoolean) {System.out.println("关闭");}// 连接异常@Overridepublic void onError(Exception e) {System.out.println("发生错误");}
}

测试websocket

package com.river.websocket;import org.java_websocket.enums.ReadyState;import java.net.URISyntaxException;/*** @author lucky_fd* @date 2024-1-17*/
public class Client {public static void main(String[] args) throws URISyntaxException, InterruptedException {MyWebSocketClient client = new MyWebSocketClient("ws://localhost:22900/websocket/server");client.connect();while (client.getReadyState() != ReadyState.OPEN) {System.out.println("连接状态:" + client.getReadyState());Thread.sleep(100);}client.send("测试数据!");client.close();}
}

参考链接:

  • 通过注解方式实现websocket:springboot集成websocket持久连接(权限过滤+拦截)
  • spring websocket实现前后端通信

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

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

相关文章

互联网加竞赛 基于机器视觉的停车位识别检测

简介 你是不是经常在停车场周围转来转去寻找停车位。如果你的车辆能准确地告诉你最近的停车位在哪里&#xff0c;那是不是很爽&#xff1f;事实证明&#xff0c;基于深度学习和OpenCV解决这个问题相对容易&#xff0c;只需获取停车场的实时视频即可。 该项目较为新颖&#xf…

2023 IoTDB Summit:北京城建智控科技股份有限公司高级研发主管刘喆《IoTDB在城市轨道交通综合监控系统中的应用》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

(设置非自定义Bean)学习Spring的第六天

一 . 获取Bean的方法详解 , 如下图 : 二 . Spring配置非自定义bean----DruidDatasource 我们举个例子 : 配置Druid数据源交由Spring管理 首先导入在pom文件Druid坐标 然后考虑 : 被配置的Bean的实例化方式是什么 : 无参构造 被配置的Bena是否要注入必要属性 : 四个基本信息…

lvgl chart 清空series数据

lvgl7 可以使用lv_chart_clear_series函数解决&#xff0c;那么lvgl8并没有相对应的series函数清除&#xff0c;反复看lvgl8代码&#xff0c;我发现可以通过设置线条全点值可以达到清除的目的 lv_chart_set_all_value(chart, series, LV_CHART_POINT_NONE); 重新通过lv_chart_…

fastJson和jackson的日期数据处理

目录 1.jackson 2.fastjson 3.总结 1.jackson jackson是spring mvc默认的JSON解析方法&#xff0c;前端的数据序列化处理之后&#xff0c;后端经过反序列化处理可以直接使用实体对象进行接收。后端接口返回实体对象&#xff0c;经过序列化处理后前端可以接收并进行处理。 …

聚类模型评估指标

聚类模型评估指标-轮廓系数 计算样本i到同簇其它样本到平均距离ai&#xff0c;ai越小&#xff0c;说明样本i越应该被聚类到该簇&#xff08;将ai称为样本i到簇内不相似度&#xff09;&#xff1b;计算样本i到其它某簇Cj的所有样本的平均距离bij&#xff0c;称为样本i与簇Cj的…

企业设计图纸安全、企业设计图纸安全软件

设计图纸对于企业的重要性不言而喻&#xff0c;因此保障设计图纸的安全显得尤为重要。以下是企业设计图纸安全需要注意的几个方面&#xff1a; 访问控制&#xff1a;只有授权人员才能访问设计图纸&#xff0c;需要通过账号密码或者其他验证方式进行身份认证。 加密传输&#…

Windows下安装alipay-sdk-python时,pycrypto安装报错问题处理

1、安装alipay-sdk-python 时&#xff0c;保存内容如下。 Building wheels for collected packages: pycryptoBuilding wheel for pycrypto (setup.py) ... error error: subprocess-exited-with-error python setup.py bdist_wheel did not run successfully.│ exit c…

从零开始了解域名:什么是域名、域名的作用及类别

在互联网时代&#xff0c;域名作为一个网站在互联网上的身份标识&#xff0c;无论是企业或者个人建设网站&#xff0c;获取域名都是其中非常关键的一环。一个好的域名不仅便于记忆&#xff0c;还有助于强化品牌、利于宣传&#xff0c;让用户更好的找到你的网站。在下面的内容中…

基于YOLOv8深度学习的100种中草药智能识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

解锁文字魔法:探索自然语言处理的秘密——从技术揭秘到应用实战!

目录 前言 关键技术——揭密自然语言处理的秘密武器&#xff01; 领域应用——自然语言处理技术在不同领域的奇妙表演&#xff01; 超越极限——自然语言处理技术面临的顽强挑战揭秘&#xff01; 科技VS伦理——自然语言处理技术的发展与伦理社会的纠结较量&#xff01; 开…

买家福音:亚马逊鲲鹏系统全自动操作助你轻松搞定一切

我一直以来都是亚马逊的忠实用户&#xff0c;但是最近我发现了一款真正令人惊叹的工具&#xff0c;改变了我在平台上的经验。我想分享一下我的感受&#xff0c;最近&#xff0c;我得知并尝试了亚马逊鲲鹏系统&#xff0c;简直是为买家账号管理量身定制的利器。在我账号过多时&a…

yolov8的目标检测、实例分割、关节点估计的原理解析

1 YOLO时间线 这里简单列下yolo的发展时间线&#xff0c;对每个版本的提出有个时间概念。 2 yolov8 的简介 工程链接&#xff1a;https://github.com/ultralytics/ultralytics 2.1 yolov8的特点 采用了anchor free方式&#xff0c;去除了先验设置可能不佳带来的影响借鉴General…

PHP Fatal error: Unparenthesized `a ? b : c ? d : e` is not supported.

这个错误是关于三元运算符的错误 这个错误在php8.0以下的版本好像是没问题呢 PHP Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : c) ? d : e or a ? b : (c ? d : e) in /cangku/app/common.php on line 57 这个问题是 程…

一站式获取 PieCloudDB Database 产品、社区及数据库行业全动态

第一部分 PieCloudDB Database 最新动态 PieCloudDB 推出社区版全新版本 11月14日&#xff0c;PieCloudDB 再度升级&#xff0c;推出社区版全新版本&#xff0c;免费面向用户开放下载&#xff0c;新版本将支持单机和多节点部署两种方式。欢迎试用&#xff01; 下载链接&…

linux docker-compose安装失败解决

1.去github下载到本地 https://github.com/docker/compose/releases/ 2.上传到linux 服务器 mv dokcer-compose-linux-x86_64 /usr/loacal/bin/docker-compose 3.给权限 chmod x /usr/local/bin/docker-compose 4.查看是否安装成功 docker-compose -version 5.卸载 …

第14章_集合与数据结构拓展练习(前序、中序、后序遍历,线性结构,单向链表构建,单向链表及其反转,字符串压缩)

文章目录 第14章_集合与数据结构拓展练习选择填空题1、前序、中序、后序遍历2、线性结构3、其它 编程题4、单向链表构建5、单向链表及其反转6、字符串压缩 第14章_集合与数据结构拓展练习 选择填空题 1、前序、中序、后序遍历 分析&#xff1a; 完全二叉树&#xff1a; 叶结点…

Flink TaskManager内存管理机制介绍与调优总结

内存模型 因为 TaskManager 是负责执行用户代码的角色&#xff0c;一般配置 TaskManager 内存的情况会比较多&#xff0c;所以本文当作重点讲解。根据实际需求为 TaskManager 配置内存将有助于减少 Flink 的资源占用&#xff0c;增强作业运行的稳定性。 TaskManager 内…

深度解析 Compose 的 Modifier 原理 -- Modifier.layout()、LayoutModifier

"Jetpack Compose - - Modifier 原理系列文章 " &#x1f4d1; 《 深度解析 Compose 的 Modifier 原理 - - Modifier、CombinedModifier 》 &#x1f4d1; 《 深度解析 Compose 的 Modifier 原理 - - Modifier.composed()、ComposedModifier 》 &#x1f4d1; 《 深度…

【Debian】非图形界面Debian10.0.0安装xfce和lxde桌面

一、安装 1. Debian10.0.0安装xfce桌面 sudo apt update sudo apt install xfce4 startxfce4 2. Debian10.0.0安装lxde桌面 sudo apt-get install lxde安装后重启电脑。 二、说明 XFCE、LXDE 和 GNOME 是三个流行的桌面环境&#xff0c;它们都是为类 Unix 操作系统设计…