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…...
Kubernetes Nginx-Ingress | 禁用HSTS/禁止重定向到https
目录 前言禁用HSTS禁止重定向到https关闭 HSTS 和设置 ssl-redirect 为 false 的区别 前言 客户请求经过ingress到服务后,默认加上了strict-transport-security,导致客户服务跨域请求失败,具体Response Headers信息如下; 分析 n…...
TortoiseGit的下载、安装和配置
一、TortoiseGit的简介 tortoiseGit是一个开放的git版本控制系统的源客户端,支持Winxp/vista/win7.该软件功能和git一样 不同的是:git是命令行操作模式,tortoiseGit界面化操作模式,不用记git相关命令就可以直接操作,读…...
如何绕过IP禁令
网站、游戏和应用程序可以屏蔽特定IP地址,从而阻止使用该IP地址的任何人访问其服务。这称为IP禁令。管理员可以出于多种原因(例如发出过多请求或可疑活动)屏蔽IP地址。但是,这些禁令会使收集数据或访问在线内容变得更加困难。 一…...
Vue3的provide和inject实现多级传递的原理
先来看个demo,这个是父组件,代码如下: <template><ChildDemo /> </template><script setup> import ChildDemo from "./child.vue"; import { ref, provide } from "vue"; // 提供响应式的值 c…...
使用html2canvas实现前端截图
一、主要功能 网页截图:html2canvas通过读取DOM结构和元素的CSS样式,在客户端生成图像,不依赖于服务端的渲染。它可以将指定的DOM元素渲染为画布(canvas),并生成图像。多种输出格式:生成的图像…...
使用 Python 爬取某网站简历模板(bs4/lxml+协程)
使用 Python 爬取站长素材简历模板 简介 在本教程中,我们将学习如何使用 Python 来爬取站长素材网站上的简历模板。我们将使用requests和BeautifulSoup库来发送 HTTP 请求和解析 HTML 页面。本教程将分为两个部分:第一部分是使用BeautifulSoup的方法&am…...
深度学习模型中音频流式处理
音频流式处理的介绍 在现代深度学习应用中,音频处理是一个重要的领域,尤其是在语音识别、音乐生成和音频分类等任务中。流式处理(Streaming Processing)是一种有效的处理方式,它允许模型逐帧处理音频数据,…...
C语言(字符数组和字符指针)
字符串实现 在C语言中,表示一个字符串有以下两种形式: 用字符数组存放一个字符串。用字符指针指向一个字符串。 案例 #include <stdio.h>/*** 方式1:使用字符数组实现字符串*/ void str_test1(){// 定义一个伪字符串char str[] &q…...
SkyWalking Helm Chart 4.7.0 安装、配置
https://skywalking.apache.org/events/release-apache-skywalking-kubernetes-helm-chart-4.7.0/https://github.com/apache/skywalking-helm/tree/v4.7.0https://skywalking.apache.org/zh/2020-04-19-skywalking-quick-start/简介 skywalking 是分布式系统的 APM(Applicat…...
微搭低代码AI组件单词消消乐从0到1实践
目录 1 为什么要开发单词消消乐2 需要具备什么功能3 采用什么技术方案实现4 逻辑设计4.1 数据结构设计4.2 游戏的核心逻辑4.3 数据设计 5 代码详解5.1 导入依赖5.2 定义函数组件5.3 数据初始化5.4 状态定义5.5 打乱解释的逻辑5.6 定义选择单词的函数5.7 定义选择解释的函数5.8 …...
用dw做电子商务网站步骤/合肥百度竞价推广代理公司
python安装完成后,它的配置很简单,只需要配置下环境变量就可以了。 具体来讲,就是将python的安装目录加入到系统的path中即可。 没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与…...
有什么教做甜品的网站/网站页面排名优化
时间很快,2016级的小鲜肉们已经在猜测老贺长什么样子了。 从在线作业到翻转课堂,几届的学生跟着我受了不少苦。话虽这么说,并不代表2016级的就要轻松了(老贺虚伪到底!)。不过,苦孩子们&…...
网站如何做访客统计/网络管理系统
python中使用多线程处理程序,会比一步步的处理节约很多时间,而且通过创建并继承Python的Thread类,重写run()方法,通过自定义的线程类来创建线程,本文介绍python多线程Thread类定义和如何自定义线程类的过程。一、Threa…...
怎样注册wordpress/百度一下首页登录
写了个VC调用PHP Sapi 的dll。功能:1.在你的vc、.net程序中嵌入PHP代码2.查看PHP的op-code用法说明:LinxPHPEmbedLib.dll导出4个函数:1.extern "C" __declspec(dllexport) void Test();说明:测试函数。在cmd下运行"…...
用地方别名做网站名/网络营销的推广方式
总结一下斯堪福三定律。 斯堪福第一定律:睡眠定律 我们应该安排好自己的时间…… 一个人如果每天睡7个小时,自然是很好的。 一个人如果每天只睡6个小时,第一天感觉会很好,觉得自己多做了很多事。 但是如果继续下去,你会…...
小米wifi设置网址入口网站/企业网站建设的目的
在VC6.0中运行控制台程序后,执行完后控制台会出现 Press any key to continue . . . 按任意键,控制台才消失。 但是换到在VC7.0 (VS.NET的VC)中编写的控制台程序,点击运行后,默认情况下马上把控制台关了,让你看…...