Websocket升级版

news2024/12/23 20:23:45

之前写过一个关于websocket的博客,是看书时候做的一个demo。但是纸上得来终觉浅,这次实战后实打实的踩了不少坑,写个博客记录总结。

1.安装postman

websocket接口调试,需要8.5以及以上版本的postman

先把以前的卸载,直接exe安装就好

点击这个连接可以下载,或者去官网下载

https://download.csdn.net/download/qq_35653822/88419296

2.postman调试方法

2.1参考这个博客的postman调试方法

Postman进行Websocket接口测试_postman websocket-CSDN博客

2.2请求路径

不是http开头  而是ws开头

如 ws://127.0.0.1:9999/test/3

2.3测试方式

websocket接口和普通接口不同,需要先建立连接,再发送消息

建立连接时候这个url可以是pathvariable那种

如 /test/{id}

3.上代码

3.1controller层

1.@ServerEndpoint
这个注解算是websocket中极其重要的一个注解了
这里边要配置上前后端建立连接的url,后端返回前端内容的解析器。我这写了两个分别对应字符串和指定实体的解析器。以及websocket对应的config文件

2. @OnOpen  @OnMessage

@OnOpen 前后端建立连接时走的方法,也就是postman点击connect按钮时候会走的方法

@OnMessage 前端给后端发送消息时走的方法,也就是postman点击send的时候会走的方法

3.session.getBasicRemote().sendObject();

后端给前端发送消息,此处根据类型不同,走不同的解析器。也就是@ServerEndpoint中的

encoders

4.注入bean

根据idea提示,需要用set方法注入 

package websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.valley.common.result.Result;
import com.valley.system.enums.LinkType;
import com.valley.system.pojo.bo.ConnectResultBO;
import com.valley.system.pojo.form.SourceForm;
import com.valley.system.service.SysDataSourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.websocket.EncodeException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.security.Principal;

/**
 * @Description: websocket  url中得sourceId是为了指定websocket得连接
 */

@ServerEndpoint(value = "/linkTest/{sourceId}", encoders = {ServerEncoder.class,StringEncoder.class},configurator = WebSocketConfig.class)
@Component
@Slf4j
public class WebSocketController {

    private static SysDataSourceService sysDataSourceService;

    @Autowired
    public void setDeviceListenerService(SysDataSourceService sysDataSourceService) {
        WebSocketController.sysDataSourceService = sysDataSourceService;
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message, Session session) throws IOException {

        session.getBasicRemote().sendText(message);
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sourceId") String sourceId) throws EncodeException, IOException {
        log.info("连接成功,sourceId:{}", sourceId);
        session.getBasicRemote().sendObject(sourceId);
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        log.info("【websocket消息】收到客户端发来的消息:{}", message);
        // 创建ObjectMapper对象
        ObjectMapper objectMapper = new ObjectMapper();

        // 将JSON字符串解析为JSON对象
        SourceForm sourceForm = objectMapper.readValue(message, SourceForm.class);
        //根据type判断是否是心跳连接
        if(LinkType.HEARTBEAT.equals(sourceForm.getLinkType())){
            //前端约定返回“pong”
            session.getBasicRemote().sendObject("pong");
            return;
        }
        ConnectResultBO bo = sysDataSourceService.linkTest(sourceForm);
        try {
            Result<ConnectResultBO> result = Result.success(bo);
            session.getBasicRemote().sendObject(result);
            //session.getBasicRemote().sendText(message.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

3.2config层

1.需要配置在controller层的@ServerEndpoint中

@ServerEndpoint(value = "/api/v1/source/linkTest/{sourceId}", encoders = {ServerEncoder.class,StringEncoder.class},configurator = WebSocketConfig.class)

2. @Configuration修饰

因为要返回其他的bean

3.websocket的token上传返回

需要取header中的 Sec-WebSocket-Protocol 属性,并且返回给前端的时候也要在header中携带

package websocket;

import cn.hutool.core.lang.Assert;
import com.valley.system.config.Constant;
import jodd.util.StringUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @Description: websocket配置
 */
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator{

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }

    /**
     * 建立握手时,连接前的操作
     */
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        // 这个userProperties 可以通过 session.getUserProperties()获取
        //获取请求头
        String token = request.getHeaders().get("Sec-WebSocket-Protocol").get(0);
        Assert.notNull(token,"token不可为空");
        //todo 之后需要对token进行解析校验
        //OAuth2AccessToken accessToken = tokenStore.readAccessToken(token);
        //前端会把空格传成&nbsp
        //token = token.replace(Constant.NBSP," ");
        //当Sec-WebSocket-Protocol请求头不为空时,需要返回给前端相同的响应
        response.getHeaders().put("Sec-WebSocket-Protocol", Collections.singletonList(token));
        super.modifyHandshake(sec, request, response);
    }

    /**
     * 初始化端点对象,也就是被@ServerEndpoint所标注的对象
     */
    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return super.getEndpointInstance(clazz);
    }
}

3.3解析器

1.如果不配置,在调用

session.getBasicRemote().sendObject(sourceId);

返回方法的时候会报此类型解析器不存在的错误

2.解析器写完之后要加到controller层的@ServerEndpoint中

@ServerEndpoint(value = "/api/v1/source/linkTest/{sourceId}", encoders = {ServerEncoder.class,StringEncoder.class},configurator = WebSocketConfig.class)

3.3.1实体解析器

package websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.valley.common.result.Result;
import com.valley.system.pojo.bo.ConnectResultBO;

import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * @Description: websocket发送给前端 泛型中Result<ConnectResultBO>是要发送的类型
 */
public class ServerEncoder implements Encoder.Text<Result<ConnectResultBO>> {

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        // 这里不重要
    }

    @Override
    public void init(EndpointConfig arg0) {
        // TODO Auto-generated method stub
        // 这里也不重要

    }

    @Override
    public String encode(Result<ConnectResultBO> responseMessage) {
        try {
            JsonMapper jsonMapper = new JsonMapper();
            return jsonMapper.writeValueAsString(responseMessage);

        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
}

 3.3.2字符串解析器

现在复盘来看,我这个解析器写的完全没必要,如果要发送字符串  把

session.getBasicRemote().sendObject

改为

session.getBasicRemote().sendText

就行了

public class StringEncoder implements Encoder.Text<String>{
    @Override
    public String encode(String s){
        return s;
    }

    @Override
    public void init(EndpointConfig endpointConfig) {

    }

    @Override
    public void destroy() {

    }
}

4.security权限过滤

这块踩了很久的坑,我们项目是开源项目改的,然后自己拆成了好几个微服务。我开始的时候只专注去改auth项目中的,不管是忽略路径permitall,还是加切面,还是加filter。都不生效。

最后,在公共项目common中找到一个关于security的配置,改了permitall,才生效

但是还要注意哈,既然在这里把权限放开了,那在config中,就得把权限校验加上

此处放开是因为,上边说过前端只能把token放到header的Sec-WebSocket-Protocol属性中,而不能像平时一样放到Authorization属性中。

 5.插曲

在进行实体转换的时候,前端多给我传了参数,引起了报错

objectMapper.readValue(message, SourceForm.class);

为了让代码健壮一点,给实体加了个注解

@JsonIgnoreProperties(ignoreUnknown = true)

Unrecognized field , not marked as ignorable解决办法-CSDN博客

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

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

相关文章

CSS 中几种常用的换行方法

1、使用 br 元素&#xff1a; 最简单的换行方法是在需要换行的位置插入 元素。例如&#xff1a; <p>This is a sentence.<br>It will be on a new line.</p>这会在 “This is a sentence.” 和 “It will be on a new line.” 之间创建一个换行。 效果&a…

需永远在线的游戏公司,如何在线替换开源存储?

小帅是一个酷爱游戏的玩家&#xff0c;他玩一款游戏已经很久了&#xff0c;始终乐在其中。 这款游戏风靡全球&#xff0c;在中国手游出海榜单中&#xff0c;长期位居榜首。 他不知道的是&#xff0c;就在他玩游戏的过程中&#xff0c;这款游戏的出品公司&#xff0c;其实已经…

Nginx解析漏洞

常见的解析漏洞&#xff1a; IIS 5.x/6.0解析漏洞 IIS 7.0/IIS 7.5/ Nginx <0.8.3畸形解析漏洞 Nginx <8.03 空字节代码执行漏洞 Apache解析漏洞 Nginx文件解析漏洞 对于任意文件名&#xff0c;例如:cd.jpg在后面添加/x.php后&#xff0c;即可将文件作为php解析。 原理…

博客文档续更

十、 博客前台模块-异常处理 目前我们的项目在认证出错或者权限不足的时候响应回来的Json&#xff0c;默认是使用Security官方提供的响应的格式&#xff0c;但是这种响应的格式肯定是不符合我们项目的接口规范的。所以需要自定义异常处理 我们需要去实现AuthenticationEntryP…

使用pyinstaller打包Python程序

文章目录 背景操作步骤使用anaconda创建虚拟环境使用pyinstaller打包代码路径问题 程序相对路径参考文献 背景 当写好python文件后&#xff0c;会希望可以打包成可执行文件&#xff0c;这样对方不需要下载python&#xff0c;双击就可以执行&#xff0c;简单方便。 为了满足这…

使用 L293D 电机驱动器 IC 和 Arduino 控制直流电机

如果您打算组装新的机器人朋友&#xff0c;您最终会想要学习如何控制直流电机。控制直流电机最简单且经济的方法是将 L293D 电机驱动器 IC 与 Arduino 连接。它可以控制两个直流电机的速度和旋转方向。 此外&#xff0c;它还可以控制单极步进电机&#xff08;如 28BYJ-48&#…

【SQL】MySQL中的索引,索引优化

索引是存储引擎用来快速查询记录的一种数据结构&#xff0c;按实现方式主要分为Hash索引和B树索引。 按功能划分&#xff0c;主要有以下几类 单列索引指的是对某一列单独建立索引&#xff0c;一张表中可以有多个单列索引 1. 单列索引 - 普通索引&#xff1a;

ubuntu 安装jdk21开发环境

下载 wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz 第二步&#xff1a;解压 tar -zxvf jdk-21_linux-x64_bin.tar.gz 第三步&#xff1a;移动 jdk-21 目录到 /usr/local/jdk21 第四步&#xff1a;配置环境变量 sudovim/etc/profile vim/etc/…

手部关键点检测2:YOLOv5实现手部检测(含训练代码和数据集)

手部关键点检测2&#xff1a;YOLOv5实现手部检测(含训练代码和数据集) 目录 手部关键点检测2&#xff1a;YOLOv5实现手部检测(含训练代码和数据集) 1. 前言 2. 手部检测数据集说明 &#xff08;1&#xff09;手部检测数据集 &#xff08;2&#xff09;自定义数据集 3. 基…

python入门篇08- 函数进阶-参数传递

全文目录,一步到位 1.前言简介1.1 专栏传送门1.1.1 上文小总结1.1.2 上文传送门 2. python基础使用2.1 函数进阶 - 参数传递2.1.1 设置多个返回值 2.2 传参方式(多种)2.1.0 代码准备2.1.1 方式一: 参数位置传递2.1.2 方式二: 关键字参数传递2.1.3 方式三: 缺省参数传递2.1.4 方…

口袋参谋:淘宝卖家必备的市场调查分析工具!

​在淘宝天猫上开店&#xff0c;首先想到的第一个问题就是——卖什么&#xff1f; 想要解决这个疑问&#xff0c;我们就需要对一些你选的品类做市场调查&#xff0c;根据市场调查分析得出了结论&#xff0c;哪个市场竞争力小&#xff0c;那就卖哪个&#xff01; 卖家做市场调查…

Linux内存管理 (2):memblock 子系统的建立

前一篇&#xff1a;Linux内存管理 (1)&#xff1a;内核镜像映射临时页表的建立 文章目录 1. 前言2. 分析背景3. memblock 简介3.1 memblock 数据结构3.2 memblock 接口 4. memblock 的构建过程 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者…

【动态规划】121. 买卖股票的最佳时机、122. 买卖股票的最佳时机 II

提示&#xff1a;努力生活&#xff0c;开心、快乐的一天 文章目录 121. 买卖股票的最佳时机&#x1f4a1;解题思路&#x1f914;遇到的问题&#x1f4bb;代码实现&#x1f3af;题目总结 122. 买卖股票的最佳时机 II&#x1f4a1;解题思路&#x1f914;遇到的问题&#x1f4bb;代…

DC电源模块工作效率的特点

BOSHIDA DC电源模块工作效率的特点 DC电源模块是一种常见的电源供应装置&#xff0c;它在广泛应用于各种电子设备中。它是一种直流电源&#xff0c;通常用于提供低压、高电流的电源&#xff0c;如电子器件、LED灯、无线路由器、计算机硬件等。DC电源模块的工作效率是其中一个非…

关于如何进行ChatGPT模型微调的新手指南

微调是指在预训练的模型基础上&#xff0c;通过进一步的训练来调整模型以适应特定任务或领域。预训练的模型在大规模的文本数据上进行了广泛的学习&#xff0c;从中获得了一定的知识和语言理解能力。然而&#xff0c;由于预训练并不针对具体任务&#xff0c;因此需要微调来使模…

启山智软/微信小程序商城

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 启山智软一、微信小程序商城二、微信小程序商城的定义微信小程序商城特点总结 启山智软 想要了解代码规范&#xff0c;学习商城解决方案&#xff0c;点击下方官网链…

11.1.0- iDesktopX新特性之统计面内对象数

作者&#xff1a;Mei 文章目录 一、属性更新二、面内统计对象数 当我们在做数据处理时&#xff0c;可能会遇到需要统计面内包含其他对象数的需求&#xff0c;在以往的iDesktopX 11i版本中&#xff0c;一般是用属性更新功能。今年发布的iDesktopX 11.1.0版本&#xff0c;有一个新…

创新学习方式,电大搜题助您迈向成功之路

近年来&#xff0c;随着信息技术的发展&#xff0c;互联网在教育领域发挥的作用越来越显著。贵州开放大学作为国内首家电视大学&#xff0c;一直致力于创新教学模式&#xff0c;帮助学生更好地获取知识。在学习过程中&#xff0c;学生常常遇到疑难问题&#xff0c;而解决这些问…

Python作业【简单算法题】

总结&#xff1a; 这次题目当中考点是基础知识&#xff0c;还有一些简单的算法&#xff0c;比如说动态规划&#xff0c;插入排序这些&#xff0c;以及切片的知识 筛选法&#xff1a; 这里说一下我对他的理解&#xff0c;之前一直不能理解为什么开个根号就可以减少算法的复杂…

【数据结构复习之路】栈和队列(本站最全最详细讲解) 严蔚敏版

复习完上面一章【线性表】&#xff0c;我们接着复习栈和队列&#xff0c;这篇文章我写的非常详细且通俗易懂&#xff0c;看完保证会带给你不一样的收获。如果对你有帮助&#xff0c;看在我这么辛苦整理的份上&#xff0c;三连一下啦ε٩(๑> ₃ <)۶з 目录: ☆ 栈 &am…