当前位置: 首页 > news >正文

spring-websocket实现(一)

websocket介绍

        WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

        WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

实现

        在spring中,使用最基础的spring-websocket的方式有两种。一种是利用注解的方式,一种是利用直接通过实现WebSocketConfigurer配置来实现。

本次先实现第一种实现WebSocketConfigurer配置来实现。

后端实现

先引入依赖:

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

 处理器

        首先,需要实现WebSocketHandler接口,WebSocketHandler定义了处理连接、处理数据、关闭连接、以及连接异常锁调用的方法,在实现类中我们可以定义需要的操作。

public class WebSocketEndPointHandler implements WebSocketHandler {private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketEndPointHandler.class);private static WebSessionSendService webSessionSendService = new WebSessionSendService();/*** 连接处理*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {LOGGER.info("WebSocketEndPointHandler:afterConnectionEstablished");List<String> appIds = session.getHandshakeHeaders().get("Sec-WebSocket-Protocol");webSessionSendService.addAppSession(appIds.get(0), session);}/*** 处理客户端发送过来的数据* @param message 消息*/@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {LOGGER.info("收到消息:{}", message.getPayload());}/*** 处理传输过程中发生的错误*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {LOGGER.info("------WebSocketEndPointHandler:handleTransportError");}/*** 处理连接关闭之后触发操作* @param closeStatus 关闭的状态信息*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {LOGGER.info("------WebSocketEndPointHandler:afterConnectionClosed");List<String> appIds = session.getHandshakeHeaders().get("Sec-WebSocket-Protocol");webSessionSendService.deleteAppSession(appIds.get(0));}@Overridepublic boolean supportsPartialMessages() {return false;}
}

        为了实现从后端将数据发送到前端。所以我们需要记录下每一个前端websocket连接的WebSocketSession。在需要发送数据的时候取出来判断并发送消息。

@Service
public class WebSessionSendService {private static ConcurrentHashMap<String, WebSocketSession> sessionPool = new ConcurrentHashMap<>();public void addAppSession(String appId, WebSocketSession session){sessionPool.put(appId, session);}public void deleteAppSession(String appId){sessionPool.remove(appId);}public void sendMessage(String appId, String message){WebSocketSession session = sessionPool.get(appId);if(session == null || !session.isOpen()){return;}TextMessage textMessage = new TextMessage(message);try {session.sendMessage(textMessage);} catch (IOException e) {throw new RuntimeException(e);}}
}

拦截器 

        其次,实现一个拦截器,用户拦截websocket过来的握手请求,这个可以根据自身需要决定要不要实现。拦截可以实现 HttpSessionHandshakeInterceptorHandshakeInterceptor都可以。

public class MessageHandshakeInterceptor extends HttpSessionHandshakeInterceptor {private static final Logger LOGGER = LoggerFactory.getLogger(MessageHandshakeInterceptor.class);@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {LOGGER.info("---------MessageHandshakeInterceptor:beforeHandshake");super.beforeHandshake(request, response, wsHandler, attributes);//这里可以拦截请求头, 做用户认证。List<String> appIds = request.getHeaders().get("Sec-WebSocket-Protocol");if(CollectionUtils.isEmpty(appIds)){LOGGER.info("websocket缺少用户认证信息");return false;}response.getHeaders().set("Sec-WebSocket-Protocol", appIds.get(0));return true;}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {LOGGER.info("--------MessageHandshakeInterceptor:afterHandshake");super.afterHandshake(request, response, wsHandler, exception);}
}

配置项

最后是配置项。通过实现WebSocketConfigurer配置相关信息。

@Configuration
@EnableWebSocket
public class PathWebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {//websocket地址以及处理器,可以多个registry.addHandler(new WebSocketEndPointHandler(), "/websocket/log/getLog").setAllowedOrigins("*")//自定义的 HandshakeHandler实现类会报错,springboot默认需要用 AbstractHandshakeHandler//.setHandshakeHandler(new WebSocketHandshakeHandler()).addInterceptors(new MessageHandshakeInterceptor());    //设置拦截器}}

前端测试代码

        我的前端代码是在自己的搭建的一个以vue为基础的项目中测试的。

<template><div class="f_c float_l"><div class="m_10"><el-input class='input_common' v-model="websocketPath" placeholder="请输入后端websocket地址" ></el-input><el-button v-if="!connected" @click=" initWebSocket" >连接</el-button><el-button v-else @click="disConnectWebsocket" >断开</el-button></div><div class="f_c" v-if="connected"><el-button @click="sendMessage">发送</el-button><el-input class='input_common mt_10' v-model="message" type="textarea" :rows="2" placeholder="请输入需要发送的消息"></el-input></div><el-divider /><div class="m_10 f_c" v-if="connected"><span class="float_l">收到消息</span><span style="border: aqua; width: 500px; height: 100px">{{receiveMessage}}</span></div></div>
</template><script>
export default {name: "index",data(){return {websocketPath:'localhost:7000/websocket-demo/websocket/log/getLog',receiveMessage:'',message:'',connected: false,ws:null,number:0,heartbeatIntervalId: null,   //客户端心跳定时发送timeoutId: null    //定时检测服务端发送过来的心跳}},methods: {initWebSocket() {this.ws = new WebSocket('ws://' + this.websocketPath, ['123456789']);this.ws.onmessage = this.websocketOnmessage;this.ws.onopen = this.websocketOpen;this.ws.onerror = this.websocketError;this.ws.onclose = this.websocketOnclose;},websocketOpen() {console.log('websocket onopen', this.ws.readyState);this.connected = true;// 开始心跳检测this.startHeartbeat();},websocketError(event) {console.log('websocket onerror', this.ws.readyState);this.connected =falseclearInterval(this.heartbeatIntervalId);clearTimeout(this.timeoutId);},websocketOnclose(event){// WebSocket关闭时的处理console.log('WebSocket disconnected');// 清除心跳检测定时器clearInterval(this.heartbeatIntervalId);clearTimeout(this.timeoutId);this.connected = false;},websocketOnmessage(event) {console.log(`收到消息:${event.data}`);let nowMessage = '第' + this.number + '次收到订阅的消息:' + event.data + '\r\n';this.receiveMessage += nowMessage;this.number++this.resetHeartbeat();},sendMessage() {if (this.ws.readyState === WebSocket.OPEN) {let param  = {message: this.message}this.ws.send(JSON.stringify(param));}},startHeartbeat() {let _that = this//开始心跳检测this.heartbeatIntervalId = setInterval( function () {//根据实际情况发送正确的心跳消息_that.ws.send('HEARTBEAT');}, 8 * 1000)this.timeoutId = setTimeout(() => {console.error('Heartbeat timeout, reconnecting...');console.log(this.ws)this.ws.close();}, 15 * 1000)},resetHeartbeat() {clearTimeout(this.timeoutId);let _that = thisthis.timeoutId = window.setTimeout(function() {console.error('Heartbeat timeout, reconnecting...');_that.ws.close();}, 15 * 1000);},disConnectWebsocket(){this.ws.close();clearInterval(this.heartbeatIntervalId);clearTimeout(this.timeoutId);}}
}
</script><style scoped></style>

启动之后就可以填写我们的websocket地址。就可以连接了。

相关文章:

  • TPS、QPS、CPS、PV和UV
  • 『FPGA通信接口』LVDS接口(4)LVDS接收端设计
  • 1095 解码PAT准考证(测试点3)
  • 海外短剧系统如何征服观众心
  • Docker可视化web工具
  • 工业web4.0UI风格令人惊艳
  • YIA主题侧边栏如何添加3D旋转标签云?
  • Spring Boot与Istio服务网格的整合实践
  • Spring Boot集成Minio插件快速入门
  • 「五度易链」企业大数据API接口开放平台上线啦!
  • EE trade:炒伦敦金的注意事项及交易指南
  • 1962springboot VUE社区服务平台系统开发mysql数据库web结构java编程计算机网页源码maven项目
  • VScode创建ROS项目 ROS集成开发环境
  • 【数学】Leetcode 69. x 的平方根【简单】
  • Linux源码阅读笔记04-实时调度类及SMP和NUMA
  • [PHP内核探索]PHP中的哈希表
  • 【391天】每日项目总结系列128(2018.03.03)
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • Laravel核心解读--Facades
  • Lucene解析 - 基本概念
  • mysql_config not found
  • springMvc学习笔记(2)
  • storm drpc实例
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 记一次删除Git记录中的大文件的过程
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 前端技术周刊 2019-02-11 Serverless
  • 嵌入式文件系统
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 试着探索高并发下的系统架构面貌
  • 在Mac OS X上安装 Ruby运行环境
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • ​2020 年大前端技术趋势解读
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #define,static,const,三种常量的区别
  • #Z0458. 树的中心2
  • (¥1011)-(一千零一拾一元整)输出
  • (1)常见O(n^2)排序算法解析
  • (2.2w字)前端单元测试之Jest详解篇
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (过滤器)Filter和(监听器)listener
  • (接口自动化)Python3操作MySQL数据库
  • (九)c52学习之旅-定时器
  • (四)linux文件内容查看
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)Dubbo快速入门、介绍、使用
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .net core 6 redis操作类
  • .NET Core 项目指定SDK版本
  • .NET Core中Emit的使用