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

唤醒手腕 Java 后端 Springboot 框架结合 socketio 学习笔记

socketio 安装配置

Socket.IO是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架,它包括了客户端的JavaScript和服务器端的Node.js。

Socket.IO除了支持WebSocket通讯协议外,还支持许多种轮询(Polling)机制以及其它实时通信方式,并封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。Socket.IO实现的Polling通信机制包括Adobe Flash Socket、AJAX长轮询、AJAX multipart streaming、持久Iframe、JSONP轮询等。Socket.IO能够根据浏览器对通讯机制的支持情况自动地选择最佳的方式来实现网络实时应用。

GitHub:https://github.com/mrniko/netty-socketio

安装 netty-socketio 依赖

<dependency><groupId>com.corundumstudio.socketio</groupId><artifactId>netty-socketio</artifactId><version>1.7.23</version>
</dependency>

在这里插入图片描述

这个配置写在服务端,客户端不用写,主要是一些 socket.io 的配置信息。

#socket.io 配置
socketio.host=127.0.0.1(别写 localhost,写服务器的 ip)
socketio.port=9999
# 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
socketio.maxFramePayloadLength=1048576
# 设置 http 交互最大内容长度
socketio.maxHttpContentLength=1048576
# socket连接数大小(如只监听一个端口 boss 线程组为 1 即可)
socketio.bossCount=1
socketio.workCount=100
socketio.allowCustomRequests=true
# 协议升级超时时间(毫秒),默认 10 秒。HTTP握手升级为 ws 协议超时时间
socketio.upgradeTimeout=1000000
# Ping 消息超时时间(毫秒),默认 60 秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
socketio.pingTimeout=6000000
# Ping 消息间隔(毫秒),默认 25 秒。客户端向服务器发送一条心跳消息间隔
socketio.pingInterval=25000

创建 Socketio 配置类

package com.mslmsxp.mathscat.config;import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class SocketioConfig {@Value("${socketio.host}")private String host;@Value("${socketio.port}")private Integer port;@Value("${socketio.bossCount}")private int bossCount;@Value("${socketio.workCount}")private int workCount;@Value("${socketio.allowCustomRequests}")private boolean allowCustomRequests;@Value("${socketio.upgradeTimeout}")private int upgradeTimeout;@Value("${socketio.pingTimeout}")private int pingTimeout;@Value("${socketio.pingInterval}")private int pingInterval;@Beanpublic SocketIOServer socketIOServer() {SocketConfig socketConfig = new SocketConfig();socketConfig.setTcpNoDelay(true);socketConfig.setSoLinger(0);com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();config.setSocketConfig(socketConfig);config.setHostname(host);config.setPort(port);config.setBossThreads(bossCount);config.setWorkerThreads(workCount);config.setAllowCustomRequests(allowCustomRequests);config.setUpgradeTimeout(upgradeTimeout);config.setPingTimeout(pingTimeout);config.setPingInterval(pingInterval);return new SocketIOServer(config);}@Beanpublic SpringAnnotationScanner springAnnotationScanner() {return new SpringAnnotationScanner(socketIOServer());}
}

public SpringAnnotationScanner springAnnotationScanner()

用于扫描netty-socketio的注解,比如 @OnConnect、@OnEvent

注意:如果想要SocketIO 的注解生效,必须注入SpringAnnotationScanner 这个类。

说明:@OnDisconnect,@OnConnect,@OnEvent都属于SocketIO的注解,想要注解生效,则必须在配置配配置SpringAnnotationScanner;
@OnConnect: 监听客户端连接
@OnDisconnect: 监听客户端断开连接
@OnEvent (value=“text”): 用于监听客户端发送的消息,value的值就是客户端请求的唯一标识,如:socket.emit(‘text’,‘要发送的消息’);

注意:SocketIOMessageEventHandler 继承了Observable,Observable是JDK自带的观察者模式中的类,继承这个类的类说明是被观察者。

ConcurrentHashMap

ConcurrentHashMapHashMap 一样,是一个存放键值对的容器。使用 hash 算法 来获取值的地址,因此时间复杂度是 O(1)。查询非常快。
同时,ConcurrentHashMap 是线程安全的 HashMap。专门用于多线程环境。

HashMap

HashMap 是线程不安全的,因为 HashMap 中操作都没有加锁,因此在多线程环境下会导致数据覆盖之类的问题,所以,在多线程中使用 HashMap 是会抛出异常的。

HashTable

HashTable 是线程安全的,但是 HashTable 只是单纯的在 put() 方法上加上 synchronized。保证插入时阻塞其他线程的插入操作。虽然安全,但因为设计简单,所以性能低下。

ConcurrentHashMap

ConcurrentHashMap是线程安全的,ConcurrentHashMap 并非锁住整个方法,而是通过原子操作和局部加锁的方法保证了多线程的线程安全,且尽可能减少了性能损耗。

java.lang.IllegalStateException: Failed to execute CommandLineRunner
······
Caused by: java.nio.channels.UnresolvedAddressException: null

socketio 业务层

package com.mslmsxp.mathscat.socketio;import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;@Slf4j
@Component
public class SocketService{@Autowiredprivate SocketIOServer socketIoServer;@OnConnectpublic void onConnect(SocketIOClient client) {String username = client.getHandshakeData().getSingleUrlParam("username");log.info("客户端:" + client.getRemoteAddress() + "  sessionId:" + client.getSessionId() + " username: " + username + "已连接");}@OnDisconnectpublic void onDisconnect(SocketIOClient client) {log.info("客户端:" + client.getSessionId() + "断开连接");Map<String, Object> paramMap = new HashMap<>();paramMap.put("type", "disconnect");paramMap.put("sessionId", client.getSessionId().toString());log.error(paramMap.toString());}
}

socket 会话本地存储

package com.mslmsxp.mathscat.socketio;import com.corundumstudio.socketio.SocketIOClient;
import io.micrometer.common.util.StringUtils;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;@Component
public class ClientCache {private static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap = new ConcurrentHashMap<>();public void saveClient(String userId, UUID sessionId, SocketIOClient socketIOClient) {if (StringUtils.isNotBlank(userId)) {HashMap<UUID, SocketIOClient> sessionIdClientCache = concurrentHashMap.get(userId);if (sessionIdClientCache == null) {sessionIdClientCache = new HashMap<>();}sessionIdClientCache.put(sessionId, socketIOClient);concurrentHashMap.put(userId, sessionIdClientCache);}}public HashMap<UUID, SocketIOClient> getUserClient(String userId) {return concurrentHashMap.get(userId);}public void deleteSessionClient(String userId, UUID sessionId) {concurrentHashMap.get(userId).remove(sessionId);}
}

启动 socketio server

方法一:@PostConstruct 注解 注入完成 启动

package com.mslmsxp.mathscat.socketio;import com.corundumstudio.socketio.SocketIOServer;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class SocketioServer {@Autowiredprivate SocketIOServer server;@PostConstructvoid init() {server.start();System.out.println("socketio launch");}}

该注解是 Java jdk 提供的注解,而不是 Spring 框架提供的, JavaEE5 引入了 @PostConstruct 和 @PreDestroy 两个作用于 Servlet 生命周期的注解,实现 Bean 初始化之前和销毁之前的自定义操作。

该注解的方法在整个 Bean 初始化中的执行顺序

Constructor(构造方法) > @Autowired(依赖注入) > @PostConstruct(注释的初始化方法)

官方文档:https://docs.oracle.com/javase/8/docs/api/javax/annotation/PostConstruct.html

@PostConstruct 注解的功能:当依赖注入完成后用于执行初始化的方法,并且只会被执行一次。

2023-02-20T18:33:27.120+08:00  INFO 16508 --- [  restartedMain] c.c.socketio.SocketIOServer              : Session store / pubsub factory used: MemoryStoreFactory (local session store only)
2023-02-20T18:33:27.300+08:00  INFO 16508 --- [ntLoopGroup-2-1] c.c.socketio.SocketIOServer              : SocketIO server started at port: 9999
socketio launch
2023-02-20T18:33:27.490+08:00  INFO 16508 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-02-20T18:33:27.509+08:00  INFO 16508 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 5000 (http) with context path ''
2023-02-20T18:33:27.515+08:00  INFO 16508 --- [  restartedMain] c.mslmsxp.mathscat.MathscatApplication   : Started MathscatApplication in 1.594 seconds (process running for 2.074)

方法二:使用 CommandLineRunner 启动

package com.mslmsxp.mathscat.socketio;import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class SocketioServer implements CommandLineRunner {@Autowiredprivate SocketIOServer server;@Overridepublic void run(String... args) throws Exception {this.server.start();System.out.println("Command Line Start Socketio Server");}
}

最后等 http Tomcat 启动完成之后 进行启动。

2023-02-20T18:36:58.703+08:00  INFO 31684 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-02-20T18:36:58.725+08:00  INFO 31684 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 5000 (http) with context path ''
2023-02-20T18:36:58.734+08:00  INFO 31684 --- [  restartedMain] c.mslmsxp.mathscat.MathscatApplication   : Started MathscatApplication in 1.907 seconds (process running for 2.505)
2023-02-20T18:36:58.736+08:00  INFO 31684 --- [  restartedMain] c.c.socketio.SocketIOServer              : Session store / pubsub factory used: MemoryStoreFactory (local session store only)
Command Line Start Socketio Server
2023-02-20T18:36:58.947+08:00  INFO 31684 --- [ntLoopGroup-2-1] c.c.socketio.SocketIOServer              : SocketIO server started at port: 9999

客户端运行测试

SocketIO 官方网站:https://socket.io/zh-CN/

使用 <script> 引入

<script src="/socket.io/socket.io.js"></script>

使用 ESM 引入

<script type="module">import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js";
</script>

前端代码案例

<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Socket Client First</title>
</head><body><div>Socket Client</div><script type="module">import { io } from "https://cdn.socket.io/4.3.2/socket.io.esm.min.js";const socket = io.connect("ws://localhost:3000?username=helloworld")socket.on("connection", (socket) => {console.log(socket.id); // x8WIv7-mJelg7on_ALbx});socket.on("connect", () => {console.log(socket.id); // x8WIv7-mJelg7on_ALbx});socket.on("disconnect", () => {console.log(socket.id); // undefined});</script>
</body></html>

服务器端运行结果:

2023-02-20T19:53:59.651+08:00  INFO 14856 --- [tLoopGroup-3-40] c.m.mathscat.socketio.SocketService      : 客户端:/127.0.0.1:63182  sessionId:64971bce-996e-4d2f-a235-800d3ac1d4a6 username: helloworld已连接

相关文章:

唤醒手腕 Java 后端 Springboot 框架结合 socketio 学习笔记

socketio 安装配置 Socket.IO是一个完全由JavaScript实现、基于Node.js、支持WebSocket的协议用于实时通信、跨平台的开源框架&#xff0c;它包括了客户端的JavaScript和服务器端的Node.js。 Socket.IO除了支持WebSocket通讯协议外&#xff0c;还支持许多种轮询&#xff08;P…...

C++入门:内联函数、auto关键字、基于范围for循环及指针空值nullptr

目录 一. 内联函数 1.1 内联函数的概念 1.2 内联函数的特性 1.3 内联函数和宏的优缺点对比 二. auto关键字&#xff08;C11&#xff09; 2.1 auto的功能 2.2 auto在使用时的注意事项 三. 基于范围的for循环&#xff08;C11&#xff09; 四. 指针空值nullptr&#xff08…...

Python遗传算法

1 人工智能概述 2020中国人工智能产业年会在苏州召开&#xff0c;会上发布的《中国人工智能发展报告2020》显示&#xff0c;过去十年(2011-2020) &#xff0c;中国人工智能专利申请量达389571件&#xff0c;占全球总量的74.7%&#xff0c;位居世界第一。 报告指出&#xff0c;…...

GEE学习笔记 六十四:绿色中国报告(个人版)

2019年上半年在遥感圈里最火的一篇文章莫过于这篇《China and India lead in greening of the world through land-use management》&#xff08;China and India lead in greening of the world through land-use management | Nature Sustainability&#xff09;&#xff0c;…...

【Kubernetes】【十八】数据存储 高级存储 配置存储

高级存储 PV和PVC ​ 前面已经学习了使用NFS提供存储&#xff0c;此时就要求用户会搭建NFS系统&#xff0c;并且会在yaml配置nfs。由于kubernetes支持的存储系统有很多&#xff0c;要求客户全都掌握&#xff0c;显然不现实。为了能够屏蔽底层存储实现的细节&#xff0c;方便用…...

传输层TCP与UDP协议

目录 传输层 传输层功能 传输层所提供的服务 传输层的两个协议 TCP协议与UDP协议 端口 端口分类 IP地址和端口的关系 UDP协议 前言&#xff1a; UDP报文格式 检验和的伪首部 伪首部内容 TCP协议 TCP报文格式 TCP协议数据段的理解 TCP的伪首部 伪首部内容 标…...

字节数组的通俗解释

1.字节是通过网络传输信息&#xff08;或在硬盘或内存中存储信息&#xff09;的单位。2.在ASCII码中&#xff0c;一个英文字母&#xff08;不分大小写&#xff09;占一个字节的空间&#xff0c;一个中文汉字占两个字节的空间。注意&#xff1a;utf-8码中一个汉字占三个字节&…...

硬件学习 软件Cadence day06 原理图网表导入PCB (过程和操作的错误),开始的画板

1.新建一个制作芯片的工程 1.打开 File ->New 2.填写信息&#xff0c;设置路径 2.原理图的网表导入 1.打开这个窗口 File -> import ->Logic.. 2.确定信息 3.解决网表导入时出现的错误 1. 第一个案列 (没有找到文件 也是这个) 比如说&#xff1a; WARNING(…...

OCT 医学图像分类

目录1. OCT 图像分类2. OCT图像数据集3. OCT图像预处理4. 特征提取5. 实验结果及分析github地址: https://github.com/aishangcengloua/OCT_Classification 1. OCT 图像分类 视网膜光学相干断层扫描(OCT)是一种成像技术&#xff0c;用于捕获活体患者视网膜的高分辨率横截面。…...

华为OD机试 - 合并数组 | 机试题算法思路 【2023】

最近更新的博客 华为OD机试 - 简易压缩算法(Python) | 机试题算法思路 【2023】 华为OD机试题 - 获取最大软件版本号(JavaScript) 华为OD机试 - 猜字谜(Python) | 机试题+算法思路 【2023】 华为OD机试 - 删除指定目录(Python) | 机试题算法思路 【2023】 华为OD机试 …...

前端开发页面样式通用约定法则

代码组织 以组件为单位组织代码段;制定一致的注释规范;组件块和子组件块以及声明块之间使用一空行分隔,子组件块之间三空行分隔;如果使用了多个 CSS 文件,将其按照组件而非页面的形式分拆,因为页面会被重组,而组件只会被移动;良好的注释是非常重要的。请留出时间来描述…...

向上跳空缺口选股公式,选出回补后再启动的标的

一、向上跳空缺口选股公式 思路&#xff1a;先找出缺口&#xff0c;缺口前后有两根K线&#xff0c;缺口低价是前一根K线的最高价&#xff0c;缺口高价是后一根K线的最低价。&#xff08;如上图&#xff09;收盘价低于缺口低价&#xff0c;即实现缺口回补。回补缺口之后&#xf…...

【IoT】做短视频之前,你需要先做好内容定位

现在做内容无疑要从垂直领域入手&#xff0c;否则你就很难出圈。 干货类或者说垂直领域方向的内容&#xff0c;往往都偏向于枯燥&#xff0c;并且会涉及很多专业性的名词&#xff0c;读者理解起来会困难很多&#xff0c;阅读的兴趣也自然会降低。 这也是笔者个人开始做短视频…...

苏宁基于 AI 和图技术的智能监控体系的建设

汤泳&#xff0c;苏宁科技集团智能监控与运维产研中心总监&#xff0c;中国商业联合会智库顾问&#xff0c;致力于海量数据分析、基于深度学习的时间序列分析与预测、自然语言处理和图神经网络的研究。在应用实践中&#xff0c;通过基于 AI 的方式不断完善智能监控体系的建设&a…...

3、内存管理

文章目录1、内存的基础知识1.1、什么是内存&#xff1f;1.2、进程的运行原理--指令1.3、逻辑地址 & 物理地址1.4、从写程序到程序运行1.5、装入模块到运行1.6、装入的三种方式--绝对装入1.7、装入的三种方式--静态重定位1.8、装入的三种方式--动态重定位&#xff08;重定位…...

【蓦然回首忆Java·基础卷Ⅰ】

文章目录开端通过引用创建对象Java的数据存储方式基本类型包装类和高精度数字操作符自动递增和自动递减老生常谈的问题&#xff1a;和equals()如何重写equals方法&#xff1f;短路字面量科学计数法位运算类型转换初始化和清理方法的重载方法的重写无参构造器this与构造器垃圾收…...

类属性和对象属性

6.3 类属性和对象属性 在类定义中&#xff0c;属性按照归属分为对象属性、类属性。按照调用的私密性分为一般属性和私有属性。 6.3.1 类属性和对象属性 对象属性是最常用到的一种属性。即使我们对上面的类&#xff1a;MyClass1实例化了一个mc的对象&#xff0c;mc对象也不能…...

【TensorFlow 】查看Tensorflow和python对应版本、将现有的TensorFlow更新到指定的版本

1、查看Tensorflow和python对应版本 1.1这里我是在TensorFlow官方网址产看的 1、打开官方网址 https://pypi.org/project/tensorflow/1.1.0rc2/#files但是这个网址好像打不开&#xff0c;点击会出现这样 问题不大 输入Tensorflow然后点击搜索&#xff0c;就会跳转到https://p…...

VO、DTO、BO、PO、DO区别

VO、DTO、BO、PO、DO区别 VO&#xff1a;&#xff08;View Object&#xff09;视图对象&#xff0c;一般位于Controller层&#xff0c;用于展示视图。DTO&#xff1a;&#xff08;Data Transfer Object&#xff09;数据传输对象&#xff0c; 即RPC 接口请求或传输出去的对象&a…...

速看!!!一套能直接拿捏大厂面试官的软件测试面试宝典

3.5.1、说说你们是怎么做自动化测试的☆☆☆☆☆我们的自动化测试主要是web UI的自动化测试&#xff0c;主要用于冒烟测试和主要功能的回归测试或者主流浏览器的兼容性测试&#xff0c;作为手工测试的一种补充&#xff0c;提高测试效率&#xff0c;减少一些重复性的测试工作。1…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

Selenium 查找页面元素的方式

Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素&#xff0c;以下是主要的定位方式&#xff1a; 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...