唤醒手腕 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
ConcurrentHashMap
和 HashMap
一样,是一个存放键值对的容器。使用 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的协议用于实时通信、跨平台的开源框架,它包括了客户端的JavaScript和服务器端的Node.js。 Socket.IO除了支持WebSocket通讯协议外,还支持许多种轮询(P…...

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

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

GEE学习笔记 六十四:绿色中国报告(个人版)
2019年上半年在遥感圈里最火的一篇文章莫过于这篇《China and India lead in greening of the world through land-use management》(China and India lead in greening of the world through land-use management | Nature Sustainability),…...

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

传输层TCP与UDP协议
目录 传输层 传输层功能 传输层所提供的服务 传输层的两个协议 TCP协议与UDP协议 端口 端口分类 IP地址和端口的关系 UDP协议 前言: UDP报文格式 检验和的伪首部 伪首部内容 TCP协议 TCP报文格式 TCP协议数据段的理解 TCP的伪首部 伪首部内容 标…...
字节数组的通俗解释
1.字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位。2.在ASCII码中,一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。注意:utf-8码中一个汉字占三个字节&…...

硬件学习 软件Cadence day06 原理图网表导入PCB (过程和操作的错误),开始的画板
1.新建一个制作芯片的工程 1.打开 File ->New 2.填写信息,设置路径 2.原理图的网表导入 1.打开这个窗口 File -> import ->Logic.. 2.确定信息 3.解决网表导入时出现的错误 1. 第一个案列 (没有找到文件 也是这个) 比如说: WARNING(…...

OCT 医学图像分类
目录1. OCT 图像分类2. OCT图像数据集3. OCT图像预处理4. 特征提取5. 实验结果及分析github地址: https://github.com/aishangcengloua/OCT_Classification 1. OCT 图像分类 视网膜光学相干断层扫描(OCT)是一种成像技术,用于捕获活体患者视网膜的高分辨率横截面。…...
华为OD机试 - 合并数组 | 机试题算法思路 【2023】
最近更新的博客 华为OD机试 - 简易压缩算法(Python) | 机试题算法思路 【2023】 华为OD机试题 - 获取最大软件版本号(JavaScript) 华为OD机试 - 猜字谜(Python) | 机试题+算法思路 【2023】 华为OD机试 - 删除指定目录(Python) | 机试题算法思路 【2023】 华为OD机试 …...
前端开发页面样式通用约定法则
代码组织 以组件为单位组织代码段;制定一致的注释规范;组件块和子组件块以及声明块之间使用一空行分隔,子组件块之间三空行分隔;如果使用了多个 CSS 文件,将其按照组件而非页面的形式分拆,因为页面会被重组,而组件只会被移动;良好的注释是非常重要的。请留出时间来描述…...

向上跳空缺口选股公式,选出回补后再启动的标的
一、向上跳空缺口选股公式 思路:先找出缺口,缺口前后有两根K线,缺口低价是前一根K线的最高价,缺口高价是后一根K线的最低价。(如上图)收盘价低于缺口低价,即实现缺口回补。回补缺口之后…...

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

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

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

【蓦然回首忆Java·基础卷Ⅰ】
文章目录开端通过引用创建对象Java的数据存储方式基本类型包装类和高精度数字操作符自动递增和自动递减老生常谈的问题:和equals()如何重写equals方法?短路字面量科学计数法位运算类型转换初始化和清理方法的重载方法的重写无参构造器this与构造器垃圾收…...
类属性和对象属性
6.3 类属性和对象属性 在类定义中,属性按照归属分为对象属性、类属性。按照调用的私密性分为一般属性和私有属性。 6.3.1 类属性和对象属性 对象属性是最常用到的一种属性。即使我们对上面的类:MyClass1实例化了一个mc的对象,mc对象也不能…...

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

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

速看!!!一套能直接拿捏大厂面试官的软件测试面试宝典
3.5.1、说说你们是怎么做自动化测试的☆☆☆☆☆我们的自动化测试主要是web UI的自动化测试,主要用于冒烟测试和主要功能的回归测试或者主流浏览器的兼容性测试,作为手工测试的一种补充,提高测试效率,减少一些重复性的测试工作。1…...

Monorepo 详解:现代前端工程的架构革命
以下是一篇关于 Monorepo 技术的详细技术博客,采用 Markdown 格式,适合发布在技术社区或团队知识库中。 🧩 深入理解 Monorepo:现代项目管理的利器 在现代软件开发中,项目规模日益庞大,模块之间的依赖关系…...

基于FPGA + JESD204B协议+高速ADC数据采集系统设计
摘 要: 针对激光扫描共聚焦显微镜的大视场、高分辨率需求,为在振镜扫描的时间内获取更多数据量,设计一种基 于 FPGA 的高速数据采集系统。该系统采用 Xilinx 的 A7 系列 FPGA 作为主控芯片,同时选用 TI 公司提供的 LM…...
软件评测师 综合测试 真题笔记
计算机组成原理 用作科学计算为主的计算机,其对主机的运算速度要求很高,应该重点考虑 CPU的主频和字长,以及内存容量; 用作大型数据库处理为主的计算机,其对主机的内存容量、存取速度和外存储器的读写速度要求较高; 对…...

鸿蒙5.0项目开发——横竖屏切换开发
横竖屏切换开发 【高心星出品】 文章目录 横竖屏切换开发运行效果窗口旋转配置module.json5的orientation字段调用窗口的setPreferredOrientation方法案例代码解析Index1页面代码:EntryAbility在module.json5的配置信息:Index页面的代码信息࿱…...

vscode不满足先决条件问题的解决——vscode的老版本安装与禁止更新(附安装包)
目录 起因 vscode更新设置的关闭 安装包 结语 起因 由于主包用的系统是centos的,且版本有点老了,再加上vscode现在不支持老版本的,这对主包来说更是雪上加霜啊 但是主包看了网上很多教程,眼花缭乱,好多配置要改&…...
MySQL 如何判断某个表中是否存在某个字段
在MySQL中,判断某个表中是否存在某个字段,可以通过查询系统数据库 INFORMATION_SCHEMA.COLUMNS 实现。以下是详细步骤和示例: 方法:使用 INFORMATION_SCHEMA.COLUMNS 通过查询系统元数据表 COLUMNS,检查目标字段是否存…...

Web后端快速入门(Maven)
Maven是apche旗下的一个开源项目,是一款用于管理和构建java项目的工具。 开源项目:Welcome to The Apache Software Foundation. Maven的作用: 依赖管理(方便快捷的管理项目依赖的资源,避免版本冲突问题)…...

C++中锁与原子操作的区别及取舍策略
文章目录 锁与原子操作的基本概念锁(Lock)原子操作(Atomic Operations) 锁与原子操作的区别1. **功能**2. **性能**3. **复杂性**4. **适用场景** 锁与原子操作的取舍策略1. **简单变量操作**2. **复杂共享资源**3. **性能敏感场景…...

数据安全合规体系构建的“三道防线“
引言 "三道防线"模型架构图 #mermaid-svg-wbeppAbwa3Vb3nL2 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-icon{fill:#552222;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-text{fi…...
String 学习总结
1. 存储机制 短字符串优化(SSO, Small String Optimization) 现代标准库中的字符串实现普遍采用 SSO 技术,将长度较短(例如 ≤15 字节)的字符串数据直接存储在字符串对象内部的固定缓冲区(栈上)…...