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.hqwc.cn/news/414339.html

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

相关文章

Ubuntu 22.04.1 LTS VirtualBox7.0 解决虚拟机窗口失去焦点一段时间后,虚拟机显示不刷新问题

故障描述&#xff1a; virtualbox安装在ubuntu系统上&#xff0c;虚拟机内安装了windows操作系统。使用中发现&#xff0c;当linux系统窗口被激活&#xff0c;如firefox浏览器&#xff0c;虚拟机的显示一段时间后会暂停刷新&#xff0c;鼠标划入虚拟机窗口后&#xff0c;才会立…

mysql数据迁移报错Specified key was too long; max key length is 767 bytes

目录 场景&#xff1a; 说明&#xff1a; 疑问&#xff1a; 解决&#xff1a; 验证&#xff1a; 场景&#xff1a; 线上项目支持的过程中遇到mysql库表结构和数据由A库迁移到B库上提示Specified key was too long; max key length is 767 bytes报错&#xff0c;第一次遇到特此…

Rust之旅 - Rust概念、Windows安装、环境配置

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列专栏目录 [Java项目…

生成式对抗网络GAN

Generative Adversarial Nets由伊恩古德费洛&#xff08;Ian J.Goodfellow&#xff09;等人于2014年发表在Conference on Neural Information Processing Systems (NeurIPS)上。NeurIPS是机器学习和计算神经科学领域的顶级国际学术会议之一。 1. GAN在哪些领域大放异彩 图像生…

目标检测--02(Two Stage目标检测算法1)

Two Stage目标检测算法 R-CNN R-CNN有哪些创新点&#xff1f; 使用CNN&#xff08;ConvNet&#xff09;对 region proposals 计算 feature vectors。从经验驱动特征&#xff08;SIFT、HOG&#xff09;到数据驱动特征&#xff08;CNN feature map&#xff09;&#xff0c;提高特…

【计算机网络】(1)OSI七层模型、协议、交换技术、路由器技术

文章目录 计算机网络功能与分类计算机网络的定义计算机网络的功能计算机网络的指标计算机网络的性能指标计算机网络的非性能指标 计算机网络的分布范围以及拓扑结构划分图计算机网络分类总线型拓扑星型拓扑环形图拓扑树型拓扑分布式拓扑 通信技术信道物理信道逻辑信道 发信机OS…

基于Java网上鲜花商城系统设计与实现(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

全开源多城市同城信息小程序源码(Laravel 框架),同城分类信息发布便民小程序系统【非DZ】

同城生活分类信息小程序&#xff0c;人才招聘、房产二手 多城市地区同城分类信息发布&#xff0c;商家入驻等功能 小程序前后端代码开源无加密&#xff0c;可进行二次开发 【源码运行要求】 1、需要已认证的微信小程序 2、已备案的域名及服务器空间 推荐使用宝塔面板LinuxPHP…

C#用Convert.ToString(Int32, Int32)和Convert.Tolnt64(String, Int32)进行数值转换

目录 一、Convert.ToString(Int32, Int32) 方法 1.定义 2. 示例 二、Convert.ToInt64(String, Int32) 1.定义 2.实例 三、用Convert.ToString(Int32, Int32)和Convert.Tolnt64(String, Int32)进行数值转换 1.Main() 2.类库 3.生成效果 使用Convert.ToString(Int32…

python数字图像处理基础(八)——harris角点检测、图像尺度空间、SIFT算法

目录 harris角点检测原理函数 图像尺度空间概念局部不变性局部不变特征SIFT算法 harris角点检测 原理 Harris 角点检测是一种用于在图像中检测角点的算法。角点是图像中局部区域的交叉点或者突出的特征点。Harris 角点检测算法旨在寻找图像中对于平移、旋转和尺度变化具有不变…

媒体AI解决方案

贴合媒体业务流程 提供智能生产、多模态内容结构化、智能编解码、审核等丰富智能应用&#xff0c;贴合行业“采编存管播发”场景。 依托强大 AI 技术 联合腾讯优图、多媒体实验室、天御、微信等领先团队&#xff0c;融合互联网媒体 AI 应用经验&#xff0c;提供有效技术支撑。…

杰理AC791N编译以及生成升级固件

一、打开工程编译 首先是找到工程文件&#xff0c;如果是使用的codeblock编译器编译&#xff0c;则找到cbp文件打开工程进行编译(例如fw-AC79_AIoT_SDK\apps\wifi_camera\board\wl82\AC791N_WIFI_CAMERA.cbp),点击build选项下拉项中选择build进行编译。 二、使用脚本生成升级固…