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

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版)

文章目录 前言服务端相关配置核心代码 客户端 前言 环境&#xff1a; JDK&#xff1a;64位 Jdk1.8 SpringBoot&#xff1a;2.1.7.RELEASE 功能&#xff1a; 使用Java中原生的NIO监听端口接受客户端的数据&#xff0c;并发送数据给客户端。 服务端 相关配置 application.ym…...

QT实战--带行号的支持高亮的编辑器实现(2)

本文主要介绍了第二种实现带行号的支持高亮的编辑器的方式,基于QTextEdit实现的,支持自定义边框,背景,颜色,以及滚动条样式,支持输入变色,复制文本到里面变色,支持替换,是一个纯专业项目使用的编辑器 先上效果图: 1.头文件ContentTextEdit.h #ifndef CONTENT_TEXT_…...

(翻译)网络安全书籍推荐列表

注&#xff1a;对于所有的书籍链接&#xff0c;我都会寻找中文版重新链接&#xff0c;如无中文版&#xff0c;则按原文链接英文版。并且所有书籍名称保留英文名称 这是一个我建立的一个有关计算机安全的书籍列表&#xff0c;它们都是很有用的“计算机安全”这个主题的相关数据。…...

TcpServer 服务器优化之后,加了多线程,对心跳包进行优化

TcpServer 服务器优化之后&#xff0c;加了多线程&#xff0c;对心跳包进行优化 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报表 功能实现&#xff1a;工作台、数据导出 工作台效果图&#xff1a; 数据导出效果图&#xff1a; 在数据统计页面点击数据导出&#xff1a;生成Excel报表 1. 工作台 1.1 需求分析和设计 1.1.1 产品原…...

经纬度解析到省市区【开源】

现在业务中有需要解析经纬度到省市区。 按理说可以直接使用高德&#xff0c;百度之类的。 但是老板太抠。于是去找开源项目。找了一圈&#xff0c;数据都太老了&#xff0c;而且有时候编码还不匹配。 所以诞生了这个项目&#xff0c;提供完整的一套省市区编码和定位反解析。…...

bug:uniapp运行到微信开发者工具 白屏 页面空白

1、没有报错信息 2、预览和真机调试都能正常显示&#xff0c;说明代码没错 3、微信开发者工具版本已经是win7能装的最高版本了&#xff0c;1.05版 链接 不打算回滚旧版本 4、解决&#xff1a;最后改调试基础库为2.25.4解决了&#xff0c;使用更高版本的都会报错&#xff0c;所…...

旧版本 MySQL 处理字符表情写入问题

报错信息 新增数据 java.sql.SQLException: Incorrect string value: \xF0\x9F\x91\x8D\xE5\x8F... for column解决方案 老项目&#xff0c;而且是旧版本&#xff0c;且表情不影响业务&#xff0c;直接简单粗暴的过滤掉即可&#xff0c;有还原的需求也可以 toUnicode 转为字…...

vue使用v-if和:class完成条件渲染

1.使用v-if 和v-else 完成主body和暂无数据两个<tbody>标签的条件渲染(注意与v-show效果的区别) 2.v-for完成列表渲染 3.:class完成分数标红的条件控制 删哪个就传哪个的id&#xff0c;基于这个id去过滤掉相同id的项&#xff0c;把剩下的项返回 <td><a click.p…...

Docker:WARNING: Published ports are discarded when using host network mode 解决方法

在Docker中&#xff0c;使用主机网络模式&#xff08;host network mode&#xff09;时&#xff0c;容器将共享主机的网络命名空间&#xff0c;这意味着容器将直接使用主机的网络接口和端口。因此&#xff0c;当你尝试通过Docker的发布端口功能&#xff08;publish a port&…...

音视频入门基础:MPEG2-TS专题(12)—— FFmpeg源码中,把各个transport packet组合成一个Section的实现

一、引言 从《音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;9&#xff09;——FFmpeg源码中&#xff0c;解码TS Header的实现》可以知道&#xff1a;FFmpeg源码中使用handle_packet函数来处理一个transport packet&#xff08;TS包&#xff09;&#xff0c;该函数的前半…...

【数据结构】二叉树的性质和存储结构

性质 在二叉树的第i层上至多有2^{i-1}个结点,至少有1个结点 深度为k的二叉树至多有2^{k-1}个结点&#xff08;k≥1&#xff09;&#xff0c;至少有k个结点 对任何一棵二叉树T&#xff0c;如果其叶子数为n0&#xff0c;度为2的结点数为n2&#xff0c;则n0n21 具有n个结点的完…...

gbase8s之查看锁表的sql

#只能看当前锁表的sql&#xff0c;看不到历史的。 #使用方法&#xff1a;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)

一、问题描述&#xff1a;在springboot项目中的resources中新建mybatis-config.xml文件时&#xff0c;从mybatis文档中复制的代码报错&#xff1a;URI 未注册(设置 | 语言和框架 | 架构和 DTD) 二、解决&#xff1a;在Springboot项目的设置->架构和DTD中添加 红色的网址&…...

Ubuntu上使用system()函数运行不需要输入密码

使用system()运行一些终端命令的时候&#xff0c;需要sudo权限&#xff0c;也就是必须输入密码&#xff0c;那么在程序自启动的时候就无法成功启动。如果设置Ubuntu下所有操作都不需要密码&#xff0c;安全性太低&#xff0c;所以我们可以将需要用到的终端指令给予无需输入密码…...

【MySQL】数据库必备知识:全面整合表的约束与深度解析

前言&#xff1a;本节内容讲述表的约束的相关内容。 表的约束博主将会通过两篇文章进行讲解&#xff0c; 这是第一篇上半部分。 讲到了约束概念。 以及几种常见约束。下面友友们开始学习吧&#xff01; ps:友友们使用了mysql就可以放心观看喽&#xff01; 目录 表的约束概念 …...

Windows下Docker快速安装使用教程

在当今软件开发和部署的世界中&#xff0c;Docker 已经成为一个不可或缺的工具。这里不对Docker进行详细阐述&#xff0c;需要系统学习Docker的伙伴可寻求更专业详细的教程或书籍学习。本文主要讲解Windows系统下Docker安装及使用。 一、环境准备 1.1检查电脑是否开启虚拟化 …...

PTA DS 6-2 另类堆栈 (C补全函数)

6-2 另类堆栈 分数 15 全屏浏览 切换布局 作者 DS课程组 单位 浙江大学 在栈的顺序存储实现中&#xff0c;另有一种方法是将Top定义为栈顶的上一个位置。请编写程序实现这种定义下堆栈的入栈、出栈操作。如何判断堆栈为空或者满&#xff1f; 函数接口定义&#xff1a; …...

rk3568之mpp开发笔记mpp移植到开发板

前言&#xff1a; 大家好&#xff0c;今天给大家介绍的内容是rk平台的mpp编解码这块的内容&#xff0c;在rk目前看到有三套框架涉及到编解码内容&#xff1a; 1、rkmedia 2、rockit 3、mpp 这三种不同形式的编解码方式&#xff0c;后面再做详细的框架对比&#xff0c;今天我…...

Vue解决跨域问题

要解决 Vue 项目的跨域问题并通过 vue.config.js 配置代理&#xff0c;可以按照以下步骤修改 vue.config.js 文件。你提供的代码大部分已经正确&#xff0c;只需要做一些格式上的调整。以下是正确的 vue.config.js 配置&#xff1a; // vue.config.jsmodule.exports {devServ…...

Scratch 3.0二次开发实战:从零构建自定义插件

1. 为什么需要自定义Scratch插件&#xff1f; Scratch作为全球最受欢迎的少儿编程工具&#xff0c;其模块化积木设计让编程学习变得直观有趣。但你可能遇到过这种情况&#xff1a;想做一个天气预报项目&#xff0c;却发现内置积木无法获取实时天气数据&#xff1b;或者想开发一…...

python codecs

# 聊聊Python里的codecs模块 平时写Python处理文本文件&#xff0c;最常打交道的可能就是open()函数了。但不知道你有没有遇到过这种情况&#xff1a;打开一个文件&#xff0c;明明看着是中文&#xff0c;读出来却是一堆乱码。或者从某个老系统导出的数据&#xff0c;用普通方式…...

OpenClaw新手误区:Qwen3-32B部署中最易犯的5个配置错误

OpenClaw新手误区&#xff1a;Qwen3-32B部署中最易犯的5个配置错误 1. 前言&#xff1a;为什么OpenClaw新手容易踩坑&#xff1f; 第一次接触OpenClaw时&#xff0c;我被它"本地化AI智能体"的定位深深吸引。作为一个长期依赖云端API的开发者&#xff0c;能直接在本…...

实测梦幻动漫魔法工坊:用LoRA调整画风,轻松打造不同风格的动漫作品

实测梦幻动漫魔法工坊&#xff1a;用LoRA调整画风&#xff0c;轻松打造不同风格的动漫作品 1. 工具概览 梦幻动漫魔法工坊是一款基于Diffusion模型和LoRA微调技术的动漫图像生成工具。它最大的特点是通过简单的界面操作&#xff0c;就能生成各种风格的二次元图像&#xff0c;…...

OpenClaw技能市场探秘:Qwen3.5-9B生态优质技能推荐

OpenClaw技能市场探秘&#xff1a;Qwen3.5-9B生态优质技能推荐 1. 为什么需要关注OpenClaw技能市场&#xff1f; 第一次听说OpenClaw技能市场时&#xff0c;我其实有些怀疑——这不就是个插件商店吗&#xff1f;但当我真正开始使用后&#xff0c;才发现这个生态系统的独特价值…...

高效解决XCOM 2模组管理难题:Alternative Mod Launcher完整指南

高效解决XCOM 2模组管理难题&#xff1a;Alternative Mod Launcher完整指南 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_…...

OpenClaw环境搭建:Mac系统下龙虾智能体快速部署教程(M1/M2芯片适配)

OpenClaw环境搭建&#xff1a;Mac系统下龙虾智能体快速部署教程&#xff08;M1/M2芯片适配&#xff09;&#x1f4da; 本章学习目标&#xff1a;深入理解OpenClaw环境搭建的核心概念与实践方法&#xff0c;掌握关键技术要点&#xff0c;了解实际应用场景与最佳实践。本文属于《…...

乱倒渣土/建筑垃圾举报平台

https://jubao.mee.gov.cn/netreport/reportInfor/reportInfor 这里是可以直接勾选地图位置的。...

COMSOL 不同激光入射角打孔形貌设置方法 模型内容:不同激光入射角度的设置 优势:视频教学...

COMSOL 不同激光入射角打孔形貌设置方法 模型内容&#xff1a;不同激光入射角度的设置 优势&#xff1a;视频教学和模型注释清晰明了&#xff0c;各个情况都有涉及可参考性极强&#xff0c;可以修改&#xff0c;收敛性已调至最优&#xff0c;本案例可进行拓展应用服务&#xff…...

手把手教你用昇腾910B部署Qwen3-Reranker-8B,并接入Dify/RAGFlow(附完整代码)

昇腾910B实战&#xff1a;Qwen3-Reranker-8B国产化部署与RAGFlow/Dify集成指南 在国产化AI基础设施加速落地的背景下&#xff0c;华为昇腾NPU正成为替代传统GPU的重要选择。本文将完整演示如何在昇腾910B上部署Qwen3-Reranker-8B重排序模型&#xff0c;并将其无缝集成到Dify和R…...