【Springboot系列】总结websocket的几种实现方式,建议收藏

news2024/11/25 11:02:18

1、前言

websocket在java中有多种实现方式,一直没有做一个整理,今天整理下三种最常用的实现方式以及一些注意点

2、javax 实现方式

之前已经单独记录了这种方式

【SpringBoot系列】springboot websocket全套模板,省去搭建的烦恼,还有福利拿哦-CSDN博客

这种好处在于接口简单,只需要几个注解即可

支持路径参数:@ServerEndpoint("/tracking/{groupName}")

参数获取:通过map访问key进行获取

Map<String, List<String>> params = session.getRequestParameterMap();
List<String> strings = params.get("groupName");

连接方式:ws:localhost:8080/tracking/123?group=1

@ServerEndpoint("/url") 该注解用于注释服务端的类,被该注解注释的类,将会被标注为webSocket的服务类,参数value为访问的路径
@OnOpen 被该注解注释的方法,将在客户端与服务端建立连接时执行
@OnMessage 被该注解注释的方法,将在服务端收到消息时执行
@OnClose 被该注解注释的方法,将在链接关闭时执行
@OnError 被该注解注释的方法,将在链接发生错误时执行

特点:ws 容器为tomcat,受限于tomcat,性能有一定问题,在常规开发中对性能没那么高要求的情况下,没有问题

3、spring-boot-starter-websocket

3.1 实现方式

依赖

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

实现具体路径handler

package com.example.wstest;

import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.Map;

/**
 * @author 种鑫
 * @date 2024/6/17 13:41
 */
@Component
public class WsHandler extends AbstractWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        URI uri = session.getUri();
//        UrlQuery of = UrlQuery.of(uri.toString(), Charset.defaultCharset());
        UriComponents builder = UriComponentsBuilder.fromUri(uri).build();
        String path = builder.getPath();
        MultiValueMap<String, String> queryParams = builder.getQueryParams();
        Map<String, Object> attributes = session.getAttributes();
        System.out.println("新连接");
    }


    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("text msg");
    }

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        super.handleBinaryMessage(session, message);
        System.out.println("Binary msg");
    }

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        super.handlePongMessage(session, message);
        System.out.println("连接错误");
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        System.out.println("连接关闭");
    }
}

配置handler生效

@Configuration
@EnableWebSocket
public class WsConfig implements WebSocketConfigurer {
    @Resource
    WsHandler wsHandler;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(wsHandler,"/spce/{id}").setAllowedOrigins("*");
    }
}

 3.2 使用方式

获取参数需要自己构建

访问方式:ws:localhost:8080/spce/111?abc=123

3.3 原理解析

依赖Springboot web,底层同样是使用tomcat支撑Websocket

org.springframework.web.socket.config.annotation.AbstractWebSocketHandlerRegistration#getMappings

	protected final M getMappings() {
		M mappings = createMappings();
		if (this.sockJsServiceRegistration != null) {
			SockJsService sockJsService = this.sockJsServiceRegistration.getSockJsService();
			this.handlerMap.forEach((wsHandler, paths) -> {
				for (String path : paths) {
					String pathPattern = (path.endsWith("/") ? path + "**" : path + "/**");
					addSockJsServiceMapping(mappings, sockJsService, wsHandler, pathPattern);
				}
			});
		}
		else {
			HandshakeHandler handshakeHandler = getOrCreateHandshakeHandler();
			HandshakeInterceptor[] interceptors = getInterceptors();
			this.handlerMap.forEach((wsHandler, paths) -> {
				for (String path : paths) {
					addWebSocketHandlerMapping(mappings, wsHandler, handshakeHandler, interceptors, path);
				}
			});
		}

		return mappings;
	}

可以看到对于不是/结尾的路径前后都加了匹配

4、Netty  Websocket

依赖

 <dependency>
     <groupId>io.netty</groupId>
     <artifactId>netty-all</artifactId>
     <version>4.1.69.Final</version>
 </dependency>

构建服务器



@Slf4j
@Configuration
public class WsServer implements CommandLineRunner {

    private static final Integer PORT = 8888;

    @Override
    public void run(String... args) throws Exception {
        new WebSocketConfig().start();
    }

    public void start() {
        // 创建EventLoopGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);
        try {

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new HttpServerCodec());
                            // 最大数据长度
                            pipeline.addLast(new HttpObjectAggregator(65536));
                            // 添加接收websocket请求的url匹配路径
                            pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
                            // 10秒内收不到消息强制断开连接
                            // pipeline.addLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS));
                            pipeline.addLast(new WebSocketHandler());
                        }
                    });

            ChannelFuture future = serverBootstrap.bind(PORT).sync();
            log.info("websocket server started, port={}", PORT);
            // 处理 channel 的关闭,sync 方法作用是同步等待 channel 关闭
            // 阻塞
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("websocket server exception", e);
            throw new RuntimeException(e);
        } finally {
            log.info("websocket server close");
            // 关闭EventLoopGroup
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 5、总结

对于常规使用,压力不大的情况下使用javax方式,很方便也很简单

对于一些游戏服务器来说建议使用Netty这种方式,可以掌控,同时可以轻松切换socket服务器

注:2,3 两种方式在注入时候会有问题,建议使用static变量,手动注入类

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

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

相关文章

直播无线麦克风哪个好?一文揭秘无线领夹麦克风哪个牌子好!

​在人人可做自媒体的时代&#xff0c;众多普通人加入自媒体。对拍视频的自媒体人&#xff0c;好内容是基础&#xff0c;好设备是保障。想提升视频音质需专业无线麦克风。现无线麦克风品牌多&#xff0c;如何少花钱买高性价比产品是问题。作为资深自媒体人&#xff0c;我用过的…

QT实现多摄像头监控

工具使用方法&#xff1a; 1、在add camera后面输入对应摄像头的IP后&#xff0c;点击add会自动布局显示。 2、在del camera后选择一个对应IP后&#xff0c;点击del会自动删除对应摄像头的显示&#xff0c;且整体布局会自动调整。 工具使用场景&#xff1a; 测试摄像头的好坏。…

Rust 开发搜索引擎 Quickwit 替代ES,成本降低 10 倍,查询亚秒级别!

一、Quickwit 概述 Quickwit 它是云存储上最快的搜索引擎&#xff0c;它非常适合用于可观察性的云原生搜索引擎&#xff0c;如日志、跟踪以及即将推出的指标。同时也是 Datadog、Elasticsearch、Loki 和 Tempo 的开源替代方案。 二、Quickwit 功能 全文搜索和聚合查询&#…

视觉应用线扫相机速度反馈(伺服转盘)

运动控制实时总线相关内容请参考运动控制专栏&#xff0c;这里不再赘述 1、运动控制常用单位u/s运动控制单位[u/s]介绍_运动控制 unit是什么单位-CSDN博客文章浏览阅读176次。运动控制很多手册上会写这样的单位&#xff0c;这里的u是英文单词unit的缩写&#xff0c;也就是单位…

哈喽GPT-4o——对GPT-4o 提示词的思考与看法

目录 一、提示词二、常用的提示词案例1、写作助理2、改写为小红书风格3、英语翻译和改写4、论文式回答5、主题解构6、提问助手7、Nature风格润色8、结构总结9、编程助手10、充当终端/解释器 大家好&#xff0c;我是哪吒。 最近&#xff0c;ChatGPT在网络上广受欢迎&#xff0c…

持PMP证书可以免考申请CSPM-2国标证书!

一提到项目管理的专业认证&#xff0c;大家首先想到的肯定是以PMP为核心的PMI体系认证。当然也有BSI和IPMP等其他体系认证&#xff0c;但都是从国外引进的专业认证&#xff0c;我国始终缺少符合中国特色项目管理环境下的项目管理专业认证体系。 如今&#xff0c;更符合中国国情…

Python中关于电商商品数据的采集【taobao/JD/商品详情数据返回】

在Python中采集电商商品数据&#xff08;如淘宝、京东等&#xff09;通常涉及到网络爬虫&#xff08;web scraping&#xff09;或称为网络数据抓取&#xff08;web data scraping&#xff09;。由于电商平台通常会有反爬虫机制&#xff0c;因此直接抓取数据可能会遇到各种挑战&…

【Kubernetes】k8s--安全机制

机制说明 Kubernetes 作为一个分布式集群的管理工具&#xff0c;保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介&#xff0c; 也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的。 比如 kubectl 如果想向 …

[JavaScript]何为变量提升?

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/139742129 出自【进步*于辰的博客】 关于编译与解释&#xff0c;详述可查阅博文《[Java]知识点》…

SQL:按用户名复制权限

生产系统中有一个模块是管理用户及菜单权限&#xff0c;它们是由3个数据表组成&#xff0c;关系及字段如下&#xff1a; 原来为每个用户添加菜单的访问权限时都是一个一个添加&#xff0c;但今天遇到有个新来的员工&#xff0c;需要具有与另一个员工相同的权限。新建一个用户后…

SQL Server 中处理重复数据:保留最新记录的两种方案

目录 首先准备测试的数据表 方案一. 使用ROW_NUMBER()函数删除重复项 方案二. 使用临时表的方式 大家在项目开发过程中,数据库几乎是每一个后端开发者必备的技能,并且经常会遇到对于数据表重复数据的处理,一般需要去除重复保留最新的记录。今天这里给大家分享两种种方案,…

【Git】-- 添加公钥到 github 或者gitlab上

仅针对系统&#xff1a;mac os 、 unix、linux 1、检查是否有 id_rsa.pub $ cd ~ $ ls -al ~/.ssh 注意&#xff1a;若已有 id_rsa.pub&#xff0c;则必要执行 第二步&#xff0c;避免覆盖掉原有正常的公钥。 配置多个 git 账号请参考&#xff1a;同一台电脑配置多个git账…

Python工具箱系列(五十三)

​​水印 水印是一种常见的图片处理需求。当既需要展示&#xff0c;又需要保护知识产权时&#xff0c;就需要使用文字或者图片来打水印。下面的代码展示了文字水印与图片水印的过程。 ​--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown from pat…

电源小白入门学习11——反激电源电路原理

电源小白入门学习11——反激电源、正激电源 隔离电源变压器介绍反激电源 前面我们学习了BUCK、BOOST、BUCK-BOOST 等各种各样的DCDC变换器&#xff0c;但是他们都有一共同的特点&#xff0c;即能量的传输路径时一个完整的通路&#xff0c;输入与输出之间不存在电气隔离&#xf…

C++链表相关内容温习回顾——移除链表元素

本文主要对之前学过的C链表相关内容进行温习回顾&#xff0c;并以 移除链表元素 为例&#xff0c;进行应用。 关于链表的基础理论可见&#xff1a;链表理论基础 应用示例&#xff1a;LeetCode 203 移除链表元素 https://leetcode.cn/problems/remove-linked-list-elements/ 0、…

Redis作者长文总结LLMs, 能够取代99%的程序员

引言 这篇文章并不是对大型语言模型&#xff08;LLMs&#xff09;的全面回顾。很明显&#xff0c;2023年对人工智能而言是特别的一年&#xff0c;但再次强调这一点似乎毫无意义。相反&#xff0c;这篇文章旨在作为一个程序员个人的见证。自从ChatGPT问世&#xff0c;以及后来使…

定个小目标之刷LeetCode热题(22)

这道题最容易想的就是排序后再遍历&#xff0c;但是时间复杂度就不是O(n)了&#xff0c;所以还是得用更优的解法&#xff0c;直接看题解&#xff0c;它是使用了HashSet&#xff0c;遍历数组&#xff0c;对于每一个数x&#xff0c;如果不存在x - 1则进入内循环&#xff0c;否则跳…

springboot弘德图书馆座位预约管理系统-计算机毕业设计源码07028

摘 要 在面对当今培育人才计划的压力&#xff0c;人们需要汲取更多的不同领域的知识来不断扩充自己的知识层面&#xff0c;因此他们对学习的欲望不断扩大&#xff0c;图书馆作为我们的学习宝地&#xff0c;有着不可替代的地位。但是在信息化时代&#xff0c;传统模式下的图书馆…

四川蔚澜时代电子商务有限公司抖音开店靠谱吗?

在数字化浪潮席卷全球的今天&#xff0c;电子商务以其独特的魅力和优势&#xff0c;成为了推动经济发展的重要引擎。作为四川地区电商服务的佼佼者&#xff0c;四川蔚澜时代电子商务有限公司凭借其深厚的行业积淀和前瞻的战略眼光&#xff0c;专注于抖音电商服务领域&#xff0…

pytest并发执行时token异常处理问题

接前面加入钩子函数处理token复用的问题&#xff0c;只保证了用例的串联执行&#xff0c;我的部分测试用例中接入了通义千问的部分接口生成测试数据&#xff0c;七八个场景跑完差不多快要10分钟。考虑使用并发执行。 http://t.csdnimg.cn/ACexL 使用多线程和不使用耗时差距很大…