当前位置: 首页 > 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…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

ui框架-文件列表展示

ui框架-文件列表展示 介绍 UI框架的文件列表展示组件&#xff0c;可以展示文件夹&#xff0c;支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项&#xff0c;适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...

Cursor AI 账号纯净度维护与高效注册指南

Cursor AI 账号纯净度维护与高效注册指南&#xff1a;解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后&#xff0c;许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...

Axure Rp 11 安装、汉化、授权

Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接&#xff1a;https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...

Xcode 16.2 版本 pod init 报错

Xcode 版本升级到 16.2 后&#xff0c;项目执行 pod init 报错&#xff1b; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…...

【Redis】Redis 的持久化策略

目录 一、RDB 定期备份 1.2 触发方式 1.2.1 手动触发 1.2.2.1 自动触发 RDB 持久化机制的场景 1.2.2.2 检查是否触发 1.2.2.3 线上运维配置 1.3 检索工具 1.4 RDB 备份实现原理 1.5 禁用 RDB 快照 1.6 RDB 优缺点分析 二、AOF 实时备份 2.1 配置文件解析 2.2 开启…...

docker 部署redis集群 配置

docker的网络模式 网桥模式每次重启容器都有可能导致容器ip地址变化&#xff0c;需要固定ip的自己自定义网络&#xff0c;这里介绍的是默认网络模式 docker创建容器 docker run --name redis6379 -p 6379:6379 -p 16379:16379 -v /etc/redis/redis6379:/etc/redis -d --r…...