WebSocket入门与结合redis

WebSocket是什么

WebSocket 是一种用于在客户端和服务器之间建立双向通信的协议,它能实现实时、持久的连接。与传统的 HTTP 请求响应模式不同,WebSocket 在建立连接后允许客户端和服务器之间相互发送消息,直到连接关闭。由于 WebSocket 具有低延迟、双向通信和高效的特点,因此适用于多种实时应用场景。

源码在下面

相关依赖

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

 websocket必须配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket 配置*/
@Configuration
public class WebSocketConfig {/*** ServerEndpointExporter 作用** 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint** @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

一、入门例子

代码demo

import jakarta.websocket.*;
import org.springframework.stereotype.Component;import java.io.IOException;/*** @Description:* @Author: sh* @Date: 2024/12/15 00:06*/
@Component
@ServerEndpoint("/websocket/WEBSOCKET_MSG_TOPIC")
public class WebSocketServer {@OnOpenpublic void onOpen(Session session) {System.out.println("客户端已连接: " + session.getId());}@OnMessagepublic void onMessage(Session session, String message) {System.out.println("收到消息: " + message);try {session.getBasicRemote().sendText("消息已收到: " + message);} catch (IOException e) {e.printStackTrace();}}@OnClosepublic void onClose(Session session) {System.out.println("客户端已关闭: " + session.getId());}@OnErrorpublic void onError(Session session, Throwable throwable) {System.out.println("发生错误: " + throwable.getMessage());}
}

测试demo

import com.alipay.api.java_websocket.client.WebSocketClient;
import com.alipay.api.java_websocket.handshake.ServerHandshake;import java.net.URI;
import java.net.URISyntaxException;/*** @Description:* @Author: sh* @Date: 2024/12/15 00:04*/
public class WebSocketClientExample {public static void main(String[] args) throws URISyntaxException {WebSocketClient client = new WebSocketClient(new URI("ws://localhost:8080/websocket/WEBSOCKET_MSG_TOPIC")) {@Overridepublic void onOpen(ServerHandshake handshakedata) {System.out.println("连接已打开");send("Hello, Server!"); // 发送消息到 WebSocket 服务器}@Overridepublic void onMessage(String message) {System.out.println("收到消息: " + message);}@Overridepublic void onClose(int code, String reason, boolean remote) {System.out.println("连接关闭: " + reason);}@Overridepublic void onError(Exception ex) {System.out.println("发生错误: " + ex.getMessage());}};client.connect();}
}

二、进阶与redis结合

进阶demo

直接从redis中获取数据通过订阅从redis中获取数据

import com.macro.mall.websocket.listener.WebSocketSubscribeListener;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;/*** @Description:* @Author: sh* @Date: 2024/12/13 15:38*/
@Component
//@ServerEndpoint(value="/websocket/WEBSOCKET_MSG_TOPIC", decoders = MyMessageDecoder.class)
@ServerEndpoint(value="/websocket/WEBSOCKET_MSG_TOPIC")
public class JavaDemo {/*** 日志对象*/private static Logger logger = LoggerFactory.getLogger(JavaDemo.class);/*** redis消息监听者容器,此处不好直接注入*/private static RedisMessageListenerContainer redisMessageListenerContainer;private static RedisTemplate redisTemplate;@Resourcepublic void setRedisMessageListenerContainer(RedisMessageListenerContainer redisMessageListenerContainer) {JavaDemo.redisMessageListenerContainer = redisMessageListenerContainer;}@Resourcepublic void setRedisTemplate(RedisTemplate redisTemplate) {JavaDemo.redisTemplate = redisTemplate;}/*** concurrent包的线程安全Set,用来存放每个客户端对应的webSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识*/private static CopyOnWriteArraySet<JavaDemo> webSocketSet = new CopyOnWriteArraySet<>();/*** websocket订阅监听器*/private WebSocketSubscribeListener subscribeListener;@OnOpenpublic void onOpen(Session session, EndpointConfig config) {webSocketSet.add(this);subscribeListener = new WebSocketSubscribeListener();subscribeListener.setSession(session);// 设置订阅topicredisMessageListenerContainer.addMessageListener(subscribeListener, new ChannelTopic("WEBSOCKET_MSG_TOPIC"));}@OnMessagepublic void onMessage(String message, Session session) {logger.debug("get msg from websocket client: {}", message);//1获取redis数据String result = (String) redisTemplate.opsForValue().get(message);//2.订阅获取redis数据
//        Object result = null;
//        // 处理消息并准备发送给前端
//        if ("WEBSOCKET_MSG_TOPIC".equals(new String(message.getChannel()))) {
//            String responseMessage = "服务器收到的消息: " + new String(message.getBody());
//
//            result = redisTemplate.opsForValue().get(new String(message.getBody()));
//        }// 使用 Session 发送消息回客户端try {session.getBasicRemote().sendText(result.toString());} catch (IOException e) {logger.error("发送消息失败: {}", e.getMessage());}}@OnClosepublic void onClose(Session session) {// 移除session对象webSocketSet.remove(this);// 移除订阅对象redisMessageListenerContainer.removeMessageListener(subscribeListener);}@OnErrorpublic void onError(Session session, Throwable error) {}
}

redis配置

@Configuration
public class RedisConfig extends BaseRedisConfig {@Beanpublic RedisConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory();}// 配置 Redis 消息监听容器@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory,MessageListener subscribeListener,  // 注意这里是 inject 消息监听器ChannelTopic channelTopic) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(redisConnectionFactory);container.addMessageListener(subscribeListener, channelTopic);  // 订阅监听器return container;}// 配置 ChannelTopic@Beanpublic ChannelTopic channelTopic() {return new ChannelTopic("WEBSOCKET_MSG_TOPIC");  // 你可以更改为你实际需要的频道名}// 配置消息监听器(假设你的 subscribeListener 是一个 MessageListener)@Beanpublic MessageListener subscribeListener() {return new WebSocketSubscribeListener();  // 假设你有一个自定义的 MessageListener 类}
}

redis的sub监听器,监听websocket收到的消息

/*** @Description:subscribe监听器* @Author: sh* @Date: 2024/12/13 16:00*/
public class WebSocketSubscribeListener implements MessageListener {/*** 日志对象*/private Logger logger = LoggerFactory.getLogger(WebSocketSubscribeListener.class);/*** websocket连接对象* -- GETTER --*  获取websocket连接对象** @return websocket连接对象*/@Getterprivate Session session;/*** 设置websocket连接对象** @param session websocket连接对象*/public void setSession(Session session) {this.session = session;}@Overridepublic void onMessage(Message message, byte[] bytes) {// 获取消息String msg = new String(message.getBody());try {session.getBasicRemote().sendText(msg);} catch (IOException e) {throw new RuntimeException(e);}}
}

Html页面测试demo

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>WebSocket Test</title>
</head>
<body>
<h1>WebSocket 测试</h1>
<div id="status">未连接</div>
<textarea id="messages" rows="10" cols="30" readonly></textarea><br>
<input type="text" id="messageInput" placeholder="输入消息" />
<button onclick="sendMessage()">发送</button>
<button onclick="closeConnection()">关闭连接</button><script>let websocket;// 创建 WebSocket 连接function connect() {websocket = new WebSocket('ws://127.0.0.1:8080/websocket/WEBSOCKET_MSG_TOPIC'); // 连接到后端 WebSocket 服务// WebSocket 连接打开时websocket.onopen = () => {document.getElementById("status").textContent = "连接已建立";};// 处理接收到的消息websocket.onmessage = (event) => {const message = event.data;// 假设服务器发送的是 JSON 格式的消息try {const parsedMessage = JSON.parse(message);// 假设服务器返回的数据格式是 { "user": "username", "content": "message text" }document.getElementById("messages").value += `来自 ${parsedMessage.user}: ${parsedMessage.content}\n`;} catch (e) {// 如果解析失败,则显示原始消息document.getElementById("messages").value += '收到: ' + message + '\n';}};// 连接关闭时websocket.onclose = () => {document.getElementById("status").textContent = "连接已关闭";};// 连接错误时websocket.onerror = (error) => {console.error("WebSocket 错误:", error);document.getElementById("status").textContent = "连接错误";};}// 发送消息到服务器function sendMessage() {const message = document.getElementById('messageInput').value;if (websocket && websocket.readyState === WebSocket.OPEN) {websocket.send(message);document.getElementById('messageInput').value = '';  // 清空输入框document.getElementById('messageInput').focus();  // 聚焦到输入框} else {alert("WebSocket 连接未打开");}}// 关闭 WebSocket 连接function closeConnection() {if (websocket) {websocket.close();}}// 页面加载时自动连接window.onload = connect;
</script>
</body>
</html>

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

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

相关文章

Hive是什么,Hive介绍

官方网站&#xff1a;Apache Hive Hive是一个基于Hadoop的数据仓库工具&#xff0c;主要用于处理和查询存储在HDSF上的大规模数据‌。Hive通过将结构化的数据文件映射为数据库表&#xff0c;并提供类SQL的查询功能&#xff0c;使得用户可以使用SQL语句来执行复杂的​MapReduce任…

OpenHarmony和OpenVela的技术创新以及两者对比

两款有名的国内开源操作系统&#xff0c;OpenHarmony&#xff0c;OpenVela都非常的优秀。本文对二者的创新进行一个简要的介绍和对比。 一、OpenHarmony OpenHarmony具有诸多有特点的技术突破和重要贡献&#xff0c;以下是一些主要方面&#xff1a; 架构设计创新 分层架构…

Electron-Vue 开发下 dev/prod/webpack server各种路径设置汇总

背景 在实际开发中&#xff0c;我发现团队对于这几个路径的设置上是纯靠猜的&#xff0c;通过一点点地尝试来找到可行的路径&#xff0c;这是不应该的&#xff0c;我们应该很清晰地了解这几个概念&#xff0c;以下通过截图和代码进行细节讲解。 npm run dev 下的路径如何处理&…

基础入门-Web应用蜜罐系统堡垒机运维API内外接口第三方拓展架构部署影响

知识点&#xff1a; 1、基础入门-Web应用-蜜罐系统 2、基础入门-Web应用-堡垒机运维 3、基础入门-Web应用-内外API接口 4、基础入门-Web应用-第三方拓展架构 一、演示案例-Web-拓展应用-蜜罐-钓鱼诱使 蜜罐&#xff1a;https://hfish.net/ 测试系统&#xff1a;Ubuntu 20.04 …

springboot中Controller内文件上传到本地以及阿里云

上传文件的基本操作 <form action"/upload" method"post" enctype"multipart/form-data"> <h1>登录</h1> 姓名&#xff1a;<input type"text" name"username" required><br> 年龄&#xf…

智慧城市工程:相关学点、优势、未来发展

目录 相关学点&#xff1a; 智慧城市的优势 挑战与未来发展 智慧城市工程是利用现代信息技术和数据分析手段&#xff0c;提升城市管理和服务水平&#xff0c;实现城市运行的智能化、便捷化和高效化的一种新型城市发展模式。智慧城市通过整合物联网&#xff08;IoT&#xff0…

看板工具助力餐饮与酒店行业实现数字化转型,提升管理与运营效率

在餐饮与酒店行业&#xff0c;服务质量和客户体验是衡量企业成功的关键因素。随着客户需求的不断多样化以及市场竞争的加剧&#xff0c;传统的管理模式逐渐难以满足高效运营的需求。尤其在高峰期&#xff0c;如何优化内部流程、提高服务效率和响应速度&#xff0c;成为了许多餐…

2024年CCF 非专业级软件能力认证CSP-J/S 第二轮( 提高组) 染色(color)

完整题目内容可前往下方链接&#xff1a; 染色&#xff08;color&#xff09;_C_嗨信奥-玩嗨信息奥林匹克竞赛-少儿编程题库学习中心https://www.hixinao.com/tiku/cpp/show-4118.html 若需更多真题&#xff0c;可前往题库中心查找&#xff0c;题库中心涵盖白名单赛事真题&am…

OpenIPC开源FPV之Adaptive-Link天空端代码解析

OpenIPC开源FPV之Adaptive-Link天空端代码解析 1. 源由2. 框架代码2.1 消息机制2.2 超时机制 3. 报文处理3.1 special报文3.2 普通报文 4. 工作流程4.1 Profile 竞选4.2 Profile 研判4.2.1 回退策略4.2.2 保持策略 4.3 Profile 应用 5. 总结6. 参考资料7. 补充资料7.1 RSSI 和 …

labelme标签批量转换数据集json_to_dataset

文章目录 labelme标签批量转换数据集json_to_dataset转换原理单张图片转换多张图片批量转换bat脚本循环法 标注图片提取标注图片转单通道 labelme标签批量转换数据集json_to_dataset 转自labelme批量制作数据集教程。 转换原理 在安装了labelme的虚拟环境中有一个labelme_js…

Apache Kylin最简单的解析、了解

官网&#xff1a;Overview | Apache Kylin 一、Apache Kylin是什么&#xff1f; 由中国团队研发具有浓厚的中国韵味&#xff0c;使用神兽麒麟&#xff08;kylin&#xff09;为名 的一个OLAP多维数据分析引擎:&#xff08;据官方给出的数据&#xff09; 亚秒级响应&#xff…

01云计算HCIA学习笔记

笔者今年7月底考取了华为云计算方向的HCIE认证&#xff0c;回顾从IA到IE的学习和项目实战&#xff0c;想整合和分享自己的学习历程&#xff0c;欢迎志同道合的朋友们一起讨论&#xff01; 第一章 云计算概述 ICT&#xff1a;ICT是世界电信协会在2001年的全球会议中提出的一个综…

php生成图片

前提 开启dg2库 去掉前面的;注释&#xff0c;有的可能会带.dll后缀影响不大 extensiongd2代码 <?php $file imagecreate(100,50); //先生成图片资源$color imagecolorallocate($file,255,255,255); //白色$c imagecolorallocate($file,0,100,255);imagefill($file,0…

免费GIS工具箱:轻松将glb文件转换成3DTiles文件

在GIS地理信息系统领域&#xff0c;GLB文件作为GLTF文件的二进制版本&#xff0c;主要用于3D模型数据的存储和展示。然而&#xff0c;GLB文件的使用频率相对较低&#xff0c;这是因为GIS系统主要处理的是地理空间数据&#xff0c;如地图、地形、地貌、植被、水系等&#xff0c;…

游戏何如防抓包

游戏抓包是指在游戏中&#xff0c;通过抓包工具捕获和分析游戏客户端与服务器之间传输的封包数据的过程。抓包工具可实现拦截、篡改、重发、丢弃游戏的上下行数据包&#xff0c;市面上常见的抓包工具有WPE、Fiddler和Charles Proxy等。 抓包工具有两种实现方式&#xff0c;一类…

Java中的Consumer接口应该如何使用(通俗易懂图解)

应用场景&#xff1a; 第一次程序员A写好了个基础的遍历方法&#xff1a; public class Demo1 {public static void main(String[] args) {//假设main方法为程序员B写的,此时需要去调用A写好的一个遍历方法//1.如果此时B突然发现想将字符串以小写的形式打印出来&#xff0c;则…

【常微分方程讲义1.1】方程的种类发展与完备

方程在数学历史中不断发展&#xff0c;逐步趋于完备。从最初的简单代数方程到包含函数、算子甚至泛函的更复杂方程&#xff0c;数学家通过不断的扩展和深化&#xff0c;逐渐建立起更为丰富和多元的方程类型体系。方程的种类之所以不断演变&#xff0c;部分是因为解决实际问题的…

Linux下学【MySQL】表的必备操作( 配实操图和SQL语句)

绪论​ “Patience is key in life &#xff08;耐心是生活的关键&#xff09;”。本章是MySQL中非常重要且基础的知识----对表的操作。再数据库中表是存储数据的容器&#xff0c;我们通过将数据填写在表中&#xff0c;从而再从表中拿取出来使用&#xff0c;本章主要讲到表的增…

深度学习之目标检测篇——残差网络与FPN结合

特征金字塔多尺度融合特征金字塔的网络原理 这里是基于resnet网络与Fpn做的结合&#xff0c;主要把resnet中的特征层利用FPN的思想一起结合&#xff0c;实现resnet_fpn。增强目标检测backone的有效性。代码实现如下&#xff1a; import torch from torch import Tensor from c…

游戏AI实现-寻路算法(BFS)

广度优先搜索算法&#xff08;英语&#xff1a;Breadth-first search&#xff0c;缩写&#xff1a;BFS&#xff09;&#xff0c;又译作宽度优先搜索&#xff0c;或横向优先搜索&#xff0c;是一种图形搜索算法。 寻路地图搭建&#xff1a; 游戏AI实现-寻路地图搭建-CSDN博客 …