Spring Boot中使用WebSocket

news2024/11/25 4:50:15

文章目录

  • 为什么要用WebSocket?
  • WebSocket的握手阶段
  • Spring Boot中使用WebSocket
    • 添加WebSocket依赖
    • 服务器代码编写
    • WebSocketSession如何获取用户信息?
    • 创建管理类管理用户与会话
    • 客户端代码

为什么要用WebSocket?

我们往往需要一些这样的场景,服务器给客户端推送消息,如淘宝推送消息,网上聊天等,这些场景下客户端没有主动向服务器发请求,而是由服务器主动的向客户端发送消息,但是之前用的HTTP协议是一次请求一次响应

那该如何实现服务器主动的向客户端推送消息呢?

如果继续使用HTTP协议,可以基于轮询的方式实现,也就是客户端每隔一段时间给服务器发请求,看看有没有要发送给我的消息,如果有就获取到消息,如果没有就等待

上述轮询存在一定问题:

  1. 消耗更多的系统资源,客户端要频繁的向服务器发请求,而这些请求大多数是没有响应的
  2. 获取消息不够及时,只有轮询的时候(下次请求的周期)才能够获取到消息

如果提高轮询频率,则将消耗更多的系统资源,如果降低轮询频率,那获取消息就不够及时

此时可以使用WebSocket协议,WebSocket协议也是应用层协议,传输层也是基于TCP协议的,该协议可以实现服务器主动向客户端推送消息的功能

WebSocket的握手阶段

先了解一下报文格式中的几个重要信息:

  • FIN:表示是否关闭websocket
  • opcode操作码:描述了当前的websocket数据帧是起到了啥作用(0x1表示文本数据,0x2表示二进制数据)
  • MASK:是否开启掩码操作,掩码操作是为了避免缓冲区溢出
  • payload length:载荷的长度
  • payload data:载荷真正携带的数据

WebSocket协议的握手过程

在这里插入图片描述

总结如下:

  1. 客户端向服务端发一个申请建立websocket连接的HTTP请求,该请求是基于HTTP协议的,这个HTTP请求的请求头包含了重要的Header头,如Connection: upgradeUpgrade: websocket,标识要进行协议升级,并升级的协议类型为websocket
  2. 服务端收到该请求后,返回一个HTTP响应,响应状态码为101表示协议切换,并且响应也会包含重要的Header头Connection: upgradeUpgrade: websocket
  3. 客户端与服务端建立好全双工的websocket长连接,后续传输都是基于WebSocket协议

Spring Boot中使用WebSocket

添加WebSocket依赖

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

服务器代码编写

主要的步骤分为以下两步:

  1. 创建一个类作为WebSocketHandler(处理WebSocket中各个通信流程)
  2. 把上述类注册到Spring中,配置路由(关联上哪个路径对应上述的handler)

创建一个类继承TextWebSocketHandler,并添加类注解@Component将该类注册到Spring中,并重写:

  • afterConnectionEstablished:该方法会在websocket连接成功后被调用
  • handleTextMessage:该方法是在websocket收到消息的时候自动调用
  • handleTransportError:该方法是在websocket连接出现异常的时候自动调用的
  • afterConnectionClosed:该方法是在websocket连接关闭后自动调用的
@Component
public class WebSocketAPI extends TextWebSocketHandler {

    @Override //该方法会在websocket连接成功后被调用
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //WebSocketSession是websocket连接对应的会话
        System.out.println("建立连接了");
    }

    @Override //该方法是在websocket收到消息的时候自动调用
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //message 为收到的消息
        System.out.println("发送消息:"+message.toString());
        //session是个会话,里面记录了通信双方,通过session对象调用send方法实现服务器推送消息
        session.sendMessage(message);
        message.getPayload(); //获取的message字符串
    }

    @Override //该方法是在websocket连接出现异常的时候自动调用的
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //exception记录了异常信息
        System.out.println("连接出现异常了");
    }

    @Override //该方法是在websocket连接关闭后自动调用的
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //status 为关闭的状态
        System.out.println("连接关闭了");
    }
}

创建另一个类实现WebSocketConfigurer接口,在类上添加@Configuration@EnableWebSocket,并重写registerWebSocketHandlers方法

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private WebSocketAPI webSocketAPI;

    @Override //通过该方法,把创建好的Handler类注册到具体的路径上
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //当浏览器,websocket的请求路径是/test的时候,就会调用到webSocketTest中的方法
        registry.addHandler(webSocketAPI,"/websocketMessage");
    }
}

每个和服务端建立websocket连接的客户端,都会在服务器这边有与之对应的WebSocketSession对象,服务器要想给谁发消息,就必须使用谁的WebSocketSession对象调用sendMessage方法发送消息,服务器向客户端推送消息使用session.sendMessage(String message)发送消息,session为每个服务器与客户端建立的websocket会话WebSocketSession

WebSocketSession如何获取用户信息?

我们通常需要获取websocket保存的用户会话的用户信息,那如何获取到连接用户的用户信息呢?

通过注册特定的HttpSession拦截器,就可以把用户给HttpSession中添加的Attribute键值对,往WebSocketSession中也添加一份

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private WebSocketAPI webSocketAPI;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketAPI,"/websocketMessage").
                // 通过注册特定的HttpSession拦截器,就可以把
                // 用户给HttpSession中添加的Attribute键值对,往WebSocketSession中也添加一份
                addInterceptors(new HttpSessionHandshakeInterceptor());
    }
}

添加完后,可以使用WebSocketSession对象调用getAttributes().get("user")方法获取用户在HttpSession中保存的用户信息

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        User user = (User)session.getAttributes().get("user");
        System.out.println("建立连接");
    }

创建管理类管理用户与会话

可以使用HashMap来维护用户与WebSocketSession的关系,key为用户id,value为WebSocketSession对象,只是此时使用线程安全的集合类ConcurrentHashMap

@Component
public class WebSocketSessionManage {
    private ConcurrentHashMap<Integer, WebSocketSession> websocketSessions = new ConcurrentHashMap<>();

    //用户连接,添加连接关系
    public void addWebSocketSession(Integer userId,WebSocketSession webSocketSession){
        //用户已经上线,防止多开
        if(websocketSessions.containsKey(userId)){
            return;
        }
        websocketSessions.put(userId,webSocketSession);
    }

    //用户掉线,删除连接关系
    public void delWebSocketSession(Integer userId,WebSocketSession webSocketSession){
        WebSocketSession get = websocketSessions.get(userId);
        //只有关系中存在自己的连接信息,才删除
        if(get == webSocketSession){
            websocketSessions.remove(userId);
        }
    }

    //根据用户id获取WebSocketSession
    public WebSocketSession getWebSocketSession(Integer userId){
        return websocketSessions.get(userId);
    }
}

客户端代码

客户端使用WebSocket的实例调用send方法即可向服务器发送消息,发送的消息一般为json字符串,所以我们可以使用websocket.send(JSON.stringfy(js))将js对象序列换为json字符串发送

<script>
	 //websocket传输消息
	 //创建websocket实例
	let websocket = new WebSocket("ws://" + location.host + "/websocketMessage");
	//绑定一些函数
	websocket.onopen = function(){
	    console.log('建立连接')
	}
	websocket.onmessage = function(e){
	    //e.data为收到服务端推送的消息
	    //e.data为一个json字符串,可以使用JSON.prase(e.data)转换为js对象
	    let resp = JSON.prase(e.data);
	    console.log('收到消息:'+resp)
	}
	websocket.onerror = function(){
	    console.log('出现异常')
	}
	websocket.onclose = function(){
	    console.log('关闭连接')
	}
	//使用websocket实例调用send方法即可向服务器发送消息
	//注意:参数为字符串,不能为js对象
	//要发送json格式的数据,将json对象序列化为字符串,JSON.stringfy(json)
	websocket.send("hehe");
</script>

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

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

相关文章

BGP状态机

BGP协议基本概念 BGP是一种外部网管协议(EGP),与OSPF、RIP等内部网管协议(IGP)不同,其着眼点不在于自动发现网络拓扑,而在于AS之间选择最佳路由和控制路由的传播。 自治系统AS( Autonomous System) 由同一个技术管理机构管理、使用统一选路策略的一些路由器的集合。 …

使用select实现TCP并发服务器模型

文章目录 前言一、select是什么&#xff1f;1.1 高级IO模型1.2 select实现1.3 select缺点: 二、使用select实现TCP并发服务器模型1.引入库2.TCP服务器端3. TCP服务器端3. 运行结果 总结 前言 本期主要分享的是对于select的使用&#xff0c;使用select实现TCP并发服务器模型&am…

JSR-133/Java内存模型(JMM)规范

by Doug Lea, with help from members of the JMM mailing list. 原文地址 The JSR-133 Cookbook for Compiler Writers 重排序(Reorderings)Volatiles and MonitorsFinal Fields 内存屏障(Memory Barrires)屏障类别数据依赖和屏障之间的关系原子指令与屏障之间的相互作用 屏…

总结885

5月小结 数学做了1800基础部分&#xff0c; 英语背了7篇文章 每日必复习&#xff08;5分钟&#xff09; 画思维导图1~15讲 学习内容&#xff1a; 英语&#xff1a;继续 背诵《we stand on the same starting point》 数学&#xff1a;进步本题目&#xff0c;我知道为啥总会…

嵌入式调试技巧-代码自动初始化

代码自动初始化 概述 在嵌入式开发过程中&#xff0c;可能会遇到初始化代码自动初始化&#xff0c;比如RTT中就运用到这项技术。那么初始化代码是如何做到自动化调用的呢&#xff1f; 在嵌入式实际开发过程中&#xff0c;往往需要对 bsp 部分进行外设配置&#xff0c;以及一…

springboot+vue+java在线考试系统 试题库组卷系统

这次开发的精品在线试题库系统有管理员&#xff0c;教师&#xff0c;学生三个角色。管理员功能有个人中心&#xff0c;专业管理&#xff0c;学生管理&#xff0c;教师管理&#xff0c;试卷管理&#xff0c;试题管理&#xff0c;考试管理。教师可以管理试题和试卷&#xff0c;查…

基于C#制作一个贪吃蛇小游戏

基于C#制作一个贪吃蛇小游戏,简单耐玩,操作简单。 一、项目创建1.1、创建1.2、素材准备1.3、界面绘制1.4、设置定时器1.5、获取键盘事件1.6、游戏结束事件二、帮助类2.1、坐标DTO2.2、果实DTO2.3、移动DTO结语一、项目创建 1.1、创建 打开Visual Studio,右侧选择创建新项目…

AutoGPT 使用教程及上手体验(一分钟配置可用)

ChatGPT 是新一代 AI 文本助手&#xff0c;可以帮助解决我们在多个领域的问题。 在某些复杂问题上&#xff0c;ChatGPT 需要经过不断的调教与沟通&#xff0c;才能得到接近正确的答案。 当你是某个领域的专家时&#xff0c;你很容易做到这一点。 但是&#xff0c;在你不熟悉…

【Linux】文件的压缩和解压

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、压缩格式 2、压缩软件 3、tar 命令简介 4、tar 命令压缩 5、总结 1、压缩格式 在市面上有非常多的压缩格式&#xff0c;…

蓝牙技术|苹果Apple Watch新专利,可监测用户更多健康指标

根据美国商标和专利局&#xff08;USPTO&#xff09;近日公示的清单&#xff0c;苹果获得了一项 Apple Watch 相关的专利&#xff0c;可以在表带嵌入 NFC、RFID 和蓝牙等组件&#xff0c;从而实现某些特定功能。 在苹果的构想中&#xff0c;未来的 Apple Watch 可以“识别”表…

BR 5AP920.1505-01 模拟电阻式触摸屏

材料编号: 5AP920.1505-01 描述: 15英寸XGA彩色TFT显示屏模拟电阻式触摸屏安装深度小无风扇运行可以用显示链接卡或PPC300升级 自动化面板AP920&#xff0c;15英寸XGA彩色TFT显示屏&#xff0c;带触摸屏(电阻式)&#xff0c;3个USB 2.0接口&#xff0c;用于自动化面板链接的…

万方 protobuf 反序列化

protobuf 是一种轻便高效的结构化数据存储格式&#xff0c;可以用于结构化数据串行化&#xff0c;或者说序列化。 在网络传输方面&#xff0c;相比传统的json&#xff0c;有着更快、更小&#xff0c;且加密性好的特点。 在实际应用中&#xff0c;万方数据库官网发送的请求&…

03 【数据代理 事件处理】

03 【数据代理 事件处理】 1.数据代理 了解数据代理需要js的一些知识&#xff1a;Object.defineProperty()&#xff0c;属性标志&#xff0c;属性描述符&#xff0c;getter&#xff0c;setter。。。 1.1数据代理 建议学习文章地址&#xff1a; https://zh.javascript.info/p…

MathType7.4永久中文Mac+Win全平台版本

MathType7.4版是一款功能强大、专业实用、应用范围广的数学公式编辑器软件&#xff0c;这款软件采用了简体中文操作界面并且完美兼容office、wps等一系列常见办公工具&#xff0c;这样就能够很好的为相关用户省去了许多繁琐的操作步骤&#xff0c;用户在这里可以轻轻松松进行公…

最小编译器和 UI 框架「GitHub 热点速览」

作者&#xff1a;HelloGitHub-小鱼干 如果有一个关键词来概述本周的 GitHub 热门项目的话&#xff0c;大概就是 van 和 sectorc 都用到的 smallest。只不过一个是前端的响应式框架&#xff0c;一个是搞编译的 C 编译器。它们除了轻量化这个共同特点之外&#xff0c;还有好用&am…

解决无法ssh命令登录wsl问题

本地主机ssh登录wsl报错被拒绝访问 C:\Users\jiangcheng> ssh rootxxx.xx.xxx.xx -p 22 ssh: connect to host xxx.xx.xxx.xx port 22: Connection refused 解决步骤如下&#xff1a; 1&#xff0c;解决密码不对的问题 wsl默认用户名root的密码是随机的&#xff0c;需要…

【深度学习】yolov7 pytorch模型转onnx,转ncnn模型和mnn模型使用细节

文章目录 前言1.前置1.1 安装必要的库1.2 .pt 权重转ncnn 和mnn所需要的权重 2、编码C项目1.ncnn2.mnn 总结 前言 yolov7 pytorch模型转onnx&#xff0c;转ncnn模型和mnn模型使用细节&#xff0c;记录一下 git仓库&#xff1a; yolov7 https://github.com/WongKinYiu/yolov7 n…

如何基于G6进行双树流转绘制? | 京东云技术团队

1. 背景 业务背景&#xff1a;CRM系统随着各业务条线对线索精细化分配的诉求逐渐增加&#xff0c;各个条线的流向规则会越来越复杂&#xff0c;各个条线甚至整个CRM的线索流转规则急需一种树形的可视化的图来表达。 技术背景&#xff1a;在开发之前考虑了三种方案&#xff0c;…

选什么样的软件平台开发能让办公效率得到提升?

在当今快节奏的时代中&#xff0c;办公自动化发展已成为趋势&#xff0c;采用什么样的软件平台开发能让办公效率得到大大提升&#xff1f;面对众多粉丝朋友提出的问题&#xff0c;作为低代码开发平台服务商&#xff0c;流辰信息有责任有义务与大家分享好产品。因为这是能提升办…

GPT现状终于有人讲清楚了!OpenAI大牛最新演讲爆火,还得是马斯克钦点的天才

量子位 | 公众号 QbitAI 继Windows Copilot发布后&#xff0c;微软Build大会热度又被一场演讲引爆。 前特斯拉AI总监Andrej Karpathy在演讲中认为思维树&#xff08;tree of thoughts&#xff09;与AlphaGo的蒙特卡洛树搜索&#xff08;MCTS&#xff09;有异曲同工之妙&#…