SpringBoot项目监听端口接受数据(NIO版)
文章目录
- 前言
- 服务端
- 相关配置
- 核心代码
- 客户端
前言
环境:
JDK:64位 Jdk1.8
SpringBoot:2.1.7.RELEASE
功能:
使用Java中原生的NIO监听端口接受客户端的数据,并发送数据给客户端。
服务端
相关配置
application.yml
socket:port: 9991bufferSize: 2048timeout: 3000
配置类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @author qf* @since 2024/10/08 21:20*/
@Component
@ConfigurationProperties(prefix = "socket")
@Setter
@Getter
@ToString
public class NioSocketConfig {private Integer port;private Integer bufferSize;private Integer timeout;
}
核心代码
CommandLineRunner
当应用程序启动时,CommandLineRunner 接口的实现类中的 run 方法会被调用
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;/*** @author qf* @since 2024/10/08 21:25*/
@Slf4j
@Component
public class CommandLineRunnerImpl implements CommandLineRunner {private NioSocketConfig nioSocketConfig;@Autowiredpublic CommandLineRunnerImpl(NioSocketConfig nioSocketConfig) {this.nioSocketConfig = nioSocketConfig;}@Overridepublic void run(String... args) {ServerSelector serverSelector = new ServerSelector(nioSocketConfig);log.info("-----------监听端口启动成功!-----------");serverSelector.server();}
}
服务类
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;/*** NIO Socket Server* @author qf* @since 2024/10/08 21:30*/
@Slf4j
public class ServerSelector {private NioSocketConfig nioSocketConfig;public ServerSelector(NioSocketConfig nioSocketConfig){this.nioSocketConfig = nioSocketConfig;}@Beanpublic void server() {Selector selector = null;Protocol protocol = null;try {// 实例化一个信道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 将该信道绑定到指定端口serverSocketChannel.bind(new InetSocketAddress(nioSocketConfig.getPort()));// 配置信道为非阻塞模式serverSocketChannel.configureBlocking(false);// 创建一个选择器selector = Selector.open();// 将选择器注册到各个信道serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 初始化事件处理器protocol = new EchoSelectorProtocol(nioSocketConfig.getBufferSize());} catch (IOException e) {log.error("粒径设备监听10091端口时发生异常:",e);}// 不断轮询select方法,获取准备好的信道所关联的Key集while (true) {try {if (selector == null || protocol == null) {break;}Thread.sleep(100);// 一直等待,直至有信道准备好了I/O操作if (selector.select(nioSocketConfig.getTimeout()) == 0) {// 在等待信道准备的同时,也可以异步地执行其他任务,continue;}// 获取准备好的信道所关联的Key集合的iterator实例Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();// 循环取得集合中的每个键值while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();// 如果服务端信道感兴趣的I/O操作为acceptif (selectionKey.isValid() && selectionKey.isAcceptable()) {protocol.handleAccept(selectionKey);}// 如果客户端信道感兴趣的I/O操作为readif (selectionKey.isValid() && selectionKey.isReadable()) {protocol.handleRead(selectionKey);}// 如果该键值有效,并且其对应的客户端信道感兴趣的I/O操作为writeif (selectionKey.isValid() && selectionKey.isWritable()) {protocol.handleWrite(selectionKey);}// 这里需要手动从键集中移除当前的keyiterator.remove();}} catch (Exception e) {log.error("监听端口轮询selector时发生异常:",e);}}}
}
协议接口
import java.io.IOException;
import java.nio.channels.SelectionKey;/*** 该接口定义了通用TCPSelectorServer类与特定协议之间的接口,* 它把与具体协议相关的处理各种I/O的操作分离了出来,* 以使不同协议都能方便地使用这个基本的服务模式。* @author qf* @since 2024/10/08 20:30*/
public interface Protocol {//accept I/O形式void handleAccept(SelectionKey selectionKey) throws IOException;//read I/O形式void handleRead(SelectionKey selectionKey) throws IOException;//write I/O形式void handleWrite(SelectionKey selectionKey) throws IOException;
}
实现类
import lombok.extern.slf4j.Slf4j;import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;/*** @author qf* @since 2024/10/08 20:30*/
@Slf4j
public class EchoSelectorProtocol implements Protocol {private int bufSize; // 缓冲区的长度public EchoSelectorProtocol(int bufSize) {this.bufSize = bufSize;}// 服务端信道已经准备好了接收新的客户端连接public void handleAccept(SelectionKey selectionKey) {try {SocketChannel socketChannel = ((ServerSocketChannel) selectionKey.channel()).accept();socketChannel.configureBlocking(false);// 将选择器注册到连接到的客户端信道,并指定该信道key值的属性为OP_READ,同时为该信道指定关联的附件socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));} catch (IOException e) {log.error("accept异常:",e);selectionKey.cancel();}}// 客户端信道已经准备好了从信道中读取数据到缓冲区public void handleRead(SelectionKey selectionKey) {try {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();if (!(selectionKey.attachment() instanceof ByteBuffer)) {return;}// 获取该信道所关联的附件,这里为缓冲区ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();long bytesRead = socketChannel.read(byteBuffer);// 如果read()方法返回-1,说明客户端关闭了连接,那么客户端已经接收到了与自己发送字节数相等的数据,可以安全地关闭if (bytesRead == -1) {socketChannel.close();} else if (bytesRead > 0) {// 将channel改为读取状态byteBuffer.flip();String dateStr = new String(byteBuffer.array());if (true) {// 注册写事件selectionKey.interestOps(SelectionKey.OP_WRITE);selectionKey.attach("test"); // 将数据附加到SelectionKey上}byteBuffer.clear();// 如果缓冲区总读入了数据,则将该信道感兴趣的操作设置为为可读可写selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);}} catch (IOException e) {log.error("read异常,", e);selectionKey.cancel();} catch (Exception e) {log.error("read异常:", e);}}// 客户端信道已经准备好了将数据从缓冲区写入信道public void handleWrite(SelectionKey selectionKey) throws IOException {SocketChannel socketChannel = (SocketChannel) selectionKey.channel();if (selectionKey.attachment() instanceof String) {String data = (String) selectionKey.attachment(); // 获取附加的数据ByteBuffer buffer = ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8));socketChannel.write(buffer);}// 写完后取消写事件,重新注册读事件selectionKey.interestOps(SelectionKey.OP_READ);}
}
客户端
import lombok.extern.slf4j.Slf4j;import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;/** * 模拟客户端* @author qf* @date 2024/7/8 19:25 */
@Slf4j
public class NIOClient {public static void main(String[] args) throws Exception{//得到一个网络通道SocketChannel socketChannel = SocketChannel.open();//设置非阻塞socketChannel.configureBlocking(false);//提供服务器端的ip 和 端口InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9991);//连接服务器if (!socketChannel.connect(inetSocketAddress)) {while (!socketChannel.finishConnect()) {//!没有 完成连接finishConnect方法log.info("因为连接需要时间,客户端不会阻塞,可以做其它工作..");}}//...如果连接成功,就发送数据String str = "hello!";ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());//发送数据,将 buffer 数据写入 channelsocketChannel.write(buffer);// 接收服务器发送的数据int count = 0;while (true) {ByteBuffer readBuffer = ByteBuffer.allocate(1024 * 1024);count += socketChannel.read(readBuffer);if(count != 0){System.out.println(count);String data = StandardCharsets.UTF_8.decode(readBuffer).toString();System.out.println(data);String x = new String(readBuffer.array());System.out.println(x);count = 0;}readBuffer.clear();}}
}
相关文章:
SpringBoot项目监听端口接受数据(Netty版)
相关文章:
SpringBoot项目监听端口接受数据(NIO版)
文章目录 前言服务端相关配置核心代码 客户端 前言 环境: JDK:64位 Jdk1.8 SpringBoot:2.1.7.RELEASE 功能: 使用Java中原生的NIO监听端口接受客户端的数据,并发送数据给客户端。 服务端 相关配置 application.ym…...
QT实战--带行号的支持高亮的编辑器实现(2)
本文主要介绍了第二种实现带行号的支持高亮的编辑器的方式,基于QTextEdit实现的,支持自定义边框,背景,颜色,以及滚动条样式,支持输入变色,复制文本到里面变色,支持替换,是一个纯专业项目使用的编辑器 先上效果图: 1.头文件ContentTextEdit.h #ifndef CONTENT_TEXT_…...
(翻译)网络安全书籍推荐列表
注:对于所有的书籍链接,我都会寻找中文版重新链接,如无中文版,则按原文链接英文版。并且所有书籍名称保留英文名称 这是一个我建立的一个有关计算机安全的书籍列表,它们都是很有用的“计算机安全”这个主题的相关数据。…...
TcpServer 服务器优化之后,加了多线程,对心跳包进行优化
TcpServer 服务器优化之后,加了多线程,对心跳包进行优化 TcpServer.h #ifndef TCPSERVER_H #define TCPSERVER_H#include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <vector> #include <map> #…...
黑马程序员Java项目实战《苍穹外卖》Day12
苍穹外卖-day12 课程内容 工作台Apache POI导出运营数据Excel报表 功能实现:工作台、数据导出 工作台效果图: 数据导出效果图: 在数据统计页面点击数据导出:生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原…...
经纬度解析到省市区【开源】
现在业务中有需要解析经纬度到省市区。 按理说可以直接使用高德,百度之类的。 但是老板太抠。于是去找开源项目。找了一圈,数据都太老了,而且有时候编码还不匹配。 所以诞生了这个项目,提供完整的一套省市区编码和定位反解析。…...
bug:uniapp运行到微信开发者工具 白屏 页面空白
1、没有报错信息 2、预览和真机调试都能正常显示,说明代码没错 3、微信开发者工具版本已经是win7能装的最高版本了,1.05版 链接 不打算回滚旧版本 4、解决:最后改调试基础库为2.25.4解决了,使用更高版本的都会报错,所…...
旧版本 MySQL 处理字符表情写入问题
报错信息 新增数据 java.sql.SQLException: Incorrect string value: \xF0\x9F\x91\x8D\xE5\x8F... for column解决方案 老项目,而且是旧版本,且表情不影响业务,直接简单粗暴的过滤掉即可,有还原的需求也可以 toUnicode 转为字…...
vue使用v-if和:class完成条件渲染
1.使用v-if 和v-else 完成主body和暂无数据两个<tbody>标签的条件渲染(注意与v-show效果的区别) 2.v-for完成列表渲染 3.:class完成分数标红的条件控制 删哪个就传哪个的id,基于这个id去过滤掉相同id的项,把剩下的项返回 <td><a click.p…...
Docker:WARNING: Published ports are discarded when using host network mode 解决方法
在Docker中,使用主机网络模式(host network mode)时,容器将共享主机的网络命名空间,这意味着容器将直接使用主机的网络接口和端口。因此,当你尝试通过Docker的发布端口功能(publish a port&…...
音视频入门基础:MPEG2-TS专题(12)—— FFmpeg源码中,把各个transport packet组合成一个Section的实现
一、引言 从《音视频入门基础:MPEG2-TS专题(9)——FFmpeg源码中,解码TS Header的实现》可以知道:FFmpeg源码中使用handle_packet函数来处理一个transport packet(TS包),该函数的前半…...
【数据结构】二叉树的性质和存储结构
性质 在二叉树的第i层上至多有2^{i-1}个结点,至少有1个结点 深度为k的二叉树至多有2^{k-1}个结点(k≥1),至少有k个结点 对任何一棵二叉树T,如果其叶子数为n0,度为2的结点数为n2,则n0n21 具有n个结点的完…...
gbase8s之查看锁表的sql
#只能看当前锁表的sql,看不到历史的。 #使用方法:sh 脚本文件名 库名 表名 database$1 table$2 hexoncheck -pt $database:$table|grep -i partnum|awk {printf ("%x|",$3)} #echo $hex #echo ${hex%?} #ownonstat -k |grep -iE ${he…...
URI 未注册(设置 语言和框架 架构和 DTD)
一、问题描述:在springboot项目中的resources中新建mybatis-config.xml文件时,从mybatis文档中复制的代码报错:URI 未注册(设置 | 语言和框架 | 架构和 DTD) 二、解决:在Springboot项目的设置->架构和DTD中添加 红色的网址&…...
Ubuntu上使用system()函数运行不需要输入密码
使用system()运行一些终端命令的时候,需要sudo权限,也就是必须输入密码,那么在程序自启动的时候就无法成功启动。如果设置Ubuntu下所有操作都不需要密码,安全性太低,所以我们可以将需要用到的终端指令给予无需输入密码…...
【MySQL】数据库必备知识:全面整合表的约束与深度解析
前言:本节内容讲述表的约束的相关内容。 表的约束博主将会通过两篇文章进行讲解, 这是第一篇上半部分。 讲到了约束概念。 以及几种常见约束。下面友友们开始学习吧! ps:友友们使用了mysql就可以放心观看喽! 目录 表的约束概念 …...
Windows下Docker快速安装使用教程
在当今软件开发和部署的世界中,Docker 已经成为一个不可或缺的工具。这里不对Docker进行详细阐述,需要系统学习Docker的伙伴可寻求更专业详细的教程或书籍学习。本文主要讲解Windows系统下Docker安装及使用。 一、环境准备 1.1检查电脑是否开启虚拟化 …...
PTA DS 6-2 另类堆栈 (C补全函数)
6-2 另类堆栈 分数 15 全屏浏览 切换布局 作者 DS课程组 单位 浙江大学 在栈的顺序存储实现中,另有一种方法是将Top定义为栈顶的上一个位置。请编写程序实现这种定义下堆栈的入栈、出栈操作。如何判断堆栈为空或者满? 函数接口定义: …...
rk3568之mpp开发笔记mpp移植到开发板
前言: 大家好,今天给大家介绍的内容是rk平台的mpp编解码这块的内容,在rk目前看到有三套框架涉及到编解码内容: 1、rkmedia 2、rockit 3、mpp 这三种不同形式的编解码方式,后面再做详细的框架对比,今天我…...
Vue解决跨域问题
要解决 Vue 项目的跨域问题并通过 vue.config.js 配置代理,可以按照以下步骤修改 vue.config.js 文件。你提供的代码大部分已经正确,只需要做一些格式上的调整。以下是正确的 vue.config.js 配置: // vue.config.jsmodule.exports {devServ…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
