BIO,NIO,AIO编程实战

server/2024/9/18 21:07:52/ 标签: nio, bio, aio, Javaio模型实现

写在前面

源码 。
关于IO分类以及IO模型等理论知识,可以参考io之io分类和io模型这篇文章。本文主要来实现Java中相关IO模型实现程序。

1:BIO

blocking io,是Java io中对阻塞IO模型的具体实现。

因为不管是server端还是client端,都需要发送消息给对端,所以我们先来定义一个通道的处理器类负责完成消息发送的工作:

package com.dahuyou.io.model.bio;import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;public class ChannelHandler {private Socket socket;private Charset charset;public ChannelHandler(Socket socket, Charset charset) {this.socket = socket;this.charset = charset;}public void writeAndFlush(Object msg) {OutputStream out = null;try {out = socket.getOutputStream();out.write((msg.toString()).getBytes(charset));out.flush();} catch (IOException e) {e.printStackTrace();}}public Socket socket() {return socket;}}

再有就是client和server又是有所不同的,哪里不同呢?一个是通道建立成功是行为,以及读取到消息时的行为可能是不同的,所以再来定义一个适配器类来适配这些不同,并且在该类中依赖ChannleHandler,从而拥有向通道中发送消息的能力,代码如下:

package com.dahuyou.io.model.bio;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.nio.charset.Charset;public abstract class ChannelAdapter extends Thread {private Socket socket;private ChannelHandler channelHandler;private Charset charset;public ChannelAdapter(Socket socket, Charset charset) {this.socket = socket;this.charset = charset;while (!socket.isConnected()) {break;}channelHandler = new ChannelHandler(this.socket, charset);channelActive(channelHandler);}@Overridepublic void run() {try {BufferedReader input = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), charset));String str = null;while ((str = input.readLine()) != null) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}channelRead(channelHandler, str);}} catch (IOException e) {e.printStackTrace();}}// 链接通知抽象类public abstract void channelActive(ChannelHandler ctx);// 读取消息抽象类public abstract void channelRead(ChannelHandler ctx, Object msg);}

其中channelActive通道建立方法和channelRead通道数据读取方法作为钩子方法供子类来提供各自场景下的具体实现。

接着,来定义具体的client类:

package com.dahuyou.io.model.bio.client;import com.dahuyou.io.model.bio.ChannelAdapter;
import com.dahuyou.io.model.bio.ChannelHandler;
import java.net.Socket;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;public class BioClientHandler extends ChannelAdapter {public BioClientHandler(Socket socket, Charset charset) {super(socket, charset);}@Overridepublic void channelActive(ChannelHandler ctx) {System.out.println("链接报告LocalAddress:" + ctx.socket().getLocalAddress());ctx.writeAndFlush("hi! BioClient to msg for you \r\n");}@Overridepublic void channelRead(ChannelHandler ctx, Object msg) {System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);ctx.writeAndFlush("hi, 我是client,我已经收到你的消息Success!\r\n");}}

定义具体的server类:

package com.dahuyou.io.model.bio.server;import com.dahuyou.io.model.bio.ChannelAdapter;
import com.dahuyou.io.model.bio.ChannelHandler;import java.net.Socket;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;public class BioServerHandler extends ChannelAdapter {public BioServerHandler(Socket socket, Charset charset) {super(socket, charset);}@Overridepublic void channelActive(ChannelHandler ctx) {System.out.println("链接报告LocalAddress:" + ctx.socket().getLocalAddress());ctx.writeAndFlush("hi! BioServer to msg for you \r\n");}@Overridepublic void channelRead(ChannelHandler ctx, Object msg) {System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);ctx.writeAndFlush("hi 我是server, 我已经收到你的消息Success!\r\n");}}

接下来就可以定义server和client的服务启动类了,定义server启动类:

package com.dahuyou.io.model.bio.server;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;//public class BioServer extends Thread {
public class BioServer {private ServerSocket serverSocket = null;public static void main(String[] args) {BioServer bioServer = new BioServer();
//        bioServer.start();bioServer.startServer();}private void startServer() {try {serverSocket = new ServerSocket();serverSocket.bind(new InetSocketAddress(7397));System.out.println("bio server start suc!");while (true) {Socket socket = serverSocket.accept();
//                BioServerHandler handler = new BioServerHandler(socket, Charset.forName("GBK"));BioServerHandler handler = new BioServerHandler(socket, Charset.forName("UTF-8"));handler.start();}} catch (IOException e) {e.printStackTrace();}}/*@Overridepublic void run() {try {serverSocket = new ServerSocket();serverSocket.bind(new InetSocketAddress(7397));System.out.println("bio server start suc!");while (true) {Socket socket = serverSocket.accept();
//                BioServerHandler handler = new BioServerHandler(socket, Charset.forName("GBK"));BioServerHandler handler = new BioServerHandler(socket, Charset.forName("UTF-8"));handler.start();}} catch (IOException e) {e.printStackTrace();}}*/
}

定义client启动类:

package com.dahuyou.io.model.bio.client;import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;public class BioClient {public static void main(String[] args) {try {Socket socket = new Socket("192.168.10.91", 7397);System.out.println("bio client start suc!");BioClientHandler bioClientHandler = new BioClientHandler(socket, Charset.forName("utf-8"));bioClientHandler.start();} catch (IOException e) {e.printStackTrace();}}}

运行server:
在这里插入图片描述
运行client:
在这里插入图片描述
运行过程中:
在这里插入图片描述
附UML图:
在这里插入图片描述

2:NIO

new io,是Java io中对多路复用IO模型的具体实现。

因为不管是server端还是client端,都需要发送消息给对端,所以我们先来定义一个通道的处理器类负责完成消息发送的工作:

package com.dahuyou.io.model.nio;import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;public class ChannelHandler {private SocketChannel channel;private Charset charset;public ChannelHandler(SocketChannel channel, Charset charset) {this.channel = channel;this.charset = charset;}public void writeAndFlush(Object msg) {try {byte[] bytes = msg.toString().getBytes(charset);ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);writeBuffer.put(bytes);writeBuffer.flip();channel.write(writeBuffer);} catch (IOException e) {e.printStackTrace();}}public SocketChannel channel() {return channel;}
}

再有就是client和server又是有所不同的,哪里不同呢?一个是通道建立成功是行为,以及读取到消息时的行为可能是不同的,所以再来定义一个适配器类来适配这些不同,并且在该类中依赖ChannleHandler,从而拥有向通道中发送消息的能力,代码如下:

package com.dahuyou.io.model.nio;import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;public abstract class ChannelAdapter extends Thread {private Selector selector;private ChannelHandler channelHandler;private Charset charset;public ChannelAdapter(Selector selector, Charset charset) {this.selector = selector;this.charset = charset;}@Overridepublic void run() {while (true) {try {Thread.sleep(1000);selector.select(1000);  //Selects a set of keys whose corresponding channels are ready for I/OSet<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> it = selectedKeys.iterator();SelectionKey key = null;while (it.hasNext()) {key = it.next();it.remove();handleInput(key);}} catch (Exception ignore) {}}}private void handleInput(SelectionKey key) throws IOException {if (!key.isValid()) return;// 客户端SocketChannelClass<?> superclass = key.channel().getClass().getSuperclass();if (superclass == SocketChannel.class){SocketChannel socketChannel = (SocketChannel) key.channel();if (key.isConnectable()) {if (socketChannel.finishConnect()) {channelHandler = new ChannelHandler(socketChannel, charset);channelActive(channelHandler);socketChannel.register(selector, SelectionKey.OP_READ);} else {System.exit(1);}}}// 服务端ServerSocketChannelif (superclass == ServerSocketChannel.class){if (key.isAcceptable()) {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);channelHandler = new ChannelHandler(socketChannel, charset);channelActive(channelHandler);}}if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer readBuffer = ByteBuffer.allocate(1024);int readBytes = socketChannel.read(readBuffer);if (readBytes > 0) {readBuffer.flip();byte[] bytes = new byte[readBuffer.remaining()];readBuffer.get(bytes);channelRead(channelHandler, new String(bytes, charset));} else if (readBytes < 0) {key.cancel();socketChannel.close();}}}// 链接通知抽象类public abstract void channelActive(ChannelHandler ctx);// 读取消息抽象类public abstract void channelRead(ChannelHandler ctx, Object msg);}

其中channelActive通道建立方法和channelRead通道数据读取方法作为钩子方法供子类来提供各自场景下的具体实现。

接着,来定义具体的client处理器类,server处理器类:

package com.dahuyou.io.model.nio.client;import com.dahuyou.io.model.nio.ChannelAdapter;
import com.dahuyou.io.model.nio.ChannelHandler;
import java.io.IOException;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;public class NioClientHandler extends ChannelAdapter {public NioClientHandler(Selector selector, Charset charset) {super(selector, charset);}@Overridepublic void channelActive(ChannelHandler ctx) {try {System.out.println("链接报告LocalAddress:" + ctx.channel().getLocalAddress());ctx.writeAndFlush("hi! NioClient to msg for you \r\n");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void channelRead(ChannelHandler ctx, Object msg) {System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);ctx.writeAndFlush("hi i am client, already receive your message!\r\n");}}
package com.dahuyou.io.model.nio.server;import com.dahuyou.io.model.nio.ChannelAdapter;
import com.dahuyou.io.model.nio.ChannelHandler;import java.io.IOException;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;public class NioServerHandler extends ChannelAdapter {public NioServerHandler(Selector selector, Charset charset) {super(selector, charset);}@Overridepublic void channelActive(ChannelHandler ctx) {try {System.out.println("链接报告LocalAddress:" + ctx.channel().getLocalAddress());ctx.writeAndFlush("hi! NioServer to msg for you \r\n");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void channelRead(ChannelHandler ctx, Object msg) {System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 接收到消息:" + msg);ctx.writeAndFlush("hi i am nio server!\r\n");}}

server,client main类:

package com.dahuyou.io.model.nio.client;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;public class NioClient {public static void main(String[] args) throws IOException {Selector selector = Selector.open();SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);boolean isConnect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 7397));System.out.println("nio client started suc!");if (isConnect) {socketChannel.register(selector, SelectionKey.OP_READ);} else {socketChannel.register(selector, SelectionKey.OP_CONNECT);}new NioClientHandler(selector, Charset.forName("GBK")).start();}}
package com.dahuyou.io.model.nio.server;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.nio.charset.Charset;public class NioServer {private Selector selector;private ServerSocketChannel socketChannel;public static void main(String[] args) throws IOException {new NioServer().bind(7397);System.out.println("nio server started suc!");}public void bind(int port) {try {selector = Selector.open();socketChannel = ServerSocketChannel.open();socketChannel.configureBlocking(false);socketChannel.socket().bind(new InetSocketAddress(port), 1024);socketChannel.register(selector, SelectionKey.OP_ACCEPT);new NioServerHandler(selector, Charset.forName("GBK")).start();} catch (IOException e) {e.printStackTrace();}}}

运行测试:
在这里插入图片描述
附UML图:
在这里插入图片描述

3:AIO

主要类:

AsynchronousServerSocketChannel:服务端的异步通道类
AsynchronousSocketChannel:客户端的异步通道类
CompletionHandler:事件回调规范接口,连接建立事件,消息可读事件等

所以,异步其实就是基于事件驱动的方式来进行编程了,到处都是回调这种,不是很符合人类的线性思维,需要习惯下。
整体代码结构和bio以及nio比较类似,不同之处在于server类,定义了初始化ChannelInitializer,来做一下初始化的工作,其中数据读取的监听类初始化如下:

public class AioServerChannelInitializer extends ChannelInitializer {@Overrideprotected void initChannel(AsynchronousSocketChannel channel) throws Exception {
//        channel.read(ByteBuffer.allocate(1024), 10, TimeUnit.SECONDS, null, new AioServerHandler(channel, Charset.forName("GBK")));ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer, buffer, new AioServerHandler(channel, Charset.forName("GBK")));}}

这样当有数据可读时就会调用AioServerHandler,当然AioServerHandler也是CompletionHandler的子类了,也是一个事件回调类。
server启动类如下:

public class AioServer extends Thread {private AsynchronousServerSocketChannel serverSocketChannel;@Overridepublic void run() {try {serverSocketChannel = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 10));serverSocketChannel.bind(new InetSocketAddress(7397));System.out.println("dahuyou-study-netty aio server start done.");// 等待CountDownLatch latch = new CountDownLatch(1);// 通过AioServerChannelInitializer,初始化数据读取的处理器(实现了接口CompletionHandler)serverSocketChannel.accept(this, new AioServerChannelInitializer());// 防止退出latch.await();} catch (Exception e) {e.printStackTrace();}}public AsynchronousServerSocketChannel serverSocketChannel() {return serverSocketChannel;}public static void main(String[] args) {new AioServer().start();}}

client启动类如下:

package com.dahuyou.io.model.aio.client;import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.util.concurrent.Future;public class AioClient {public static void main(String[] args) throws Exception {AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
//        Future<Void> future = socketChannel.connect(new InetSocketAddress("127.0.0.1", 7397));
//        socketChannel.connect(new InetSocketAddress("127.0.0.1", 7397));// aio的a体现:指定一个回调类,建立连接成功时回调socketChannel.connect(new InetSocketAddress("127.0.0.1", 7397), null, new CompletionHandler<Void, Object>() {@Overridepublic void completed(Void result, Object attachment) {System.out.println("connect to nio server suc!");ByteBuffer allocate = ByteBuffer.allocate(1024);socketChannel.read(allocate, allocate, new AioClientHandler(socketChannel, Charset.forName("GBK")));}@Overridepublic void failed(Throwable exc, Object attachment) {}});System.out.println("dahuyou-study-netty aio client start done.");
//        future.get();// 读数据// clientChannel.read(readBuffer, readBuffer, new ReadHandler(clientChannel, latch));
//        socketChannel.read(ByteBuffer.allocate(1024), null, new AioClientHandler(socketChannel, Charset.forName("GBK")));Thread.sleep(10000000);}}

运行cient输出:

dahuyou-study-netty aio client start done.
connect to nio server suc!
链接报告信息:/127.0.0.1:7397
nio client receive: congratulations, connected to aio server suc!

server对应输出:

dahuyou-study-netty aio server start done.
链接报告信息:/127.0.0.1:63537
nio server receive:nio client send back msg!

程序有点问题,就是,server只能给client发一个消息,用netassit测试也有这个问题,对这块不太熟,哪位大哥大姐有空帮给看看,发现问题还请留言告知,感谢!!!,问题对应的代码位置如下:
在这里插入图片描述

写在后面

参考文章列表

io之io分类和io模型 。

UML一一 类图关系 (泛化、实现、依赖、关联、聚合、组合) 。


http://www.ppmy.cn/server/102904.html

相关文章

30. 串联所有单词的子串【 力扣(LeetCode) 】

一、题目描述 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words [“ab”,“cd”,“ef”]&#xff0c; 那么 “abcdef”&#xff0c;…

C++内存泄漏--**关于“异常0xc0000005 读取的位置 0xDDDDDDDD时发生冲突”

C中&#xff0c;很多方式可以引起内存泄漏&#xff0c;譬如如下代码&#xff1a; class RefClass { public:RefClass(int n_){pInt new int(n_);}~RefClass(){if(NULL ! pInt){delete pInt;pInt NULL;}}int* pInt NULL; };/*检查*p_n_是否为0&#xff0c;是的话&#xff0c…

模型量化方法-GPTQ

GPTQ 是一种高效的量化方法&#xff0c;用于大规模语言模型的量化和加速推理。GPTQ 主要目标是在不显著降低模型性能的情况下&#xff0c;最大限度地减少模型的计算复杂度和内存占用&#xff0c;从而使得这些模型可以在资源有限的硬件上运行。 GPTQ 的主要特征和优势 逐层量化…

基于SpringBoot+Vue疫情物资捐赠和分配系统--论文pf

TOC springboot518基于SpringBootVue疫情物资捐赠和分配系统--论文pf 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往…

所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配。

在c#工程里新建了一个类库&#xff0c;编译的场合出现以下警告&#xff1a;C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\amd64\Microsoft.Common.CurrentVersion.targets(2401,5): warning MSB3270: 所生成项目的处理器架构“MSIL”与引用“…

【python基础】—离线环境下,在linux中安装python包(以sqlalchemy为例)

文章目录 第一步&#xff1a;在线环境下载离线安装包及依赖包。第二步&#xff1a;把包上传到离线环境的服务器上&#xff0c;进行安装。第三步&#xff1a;测试是否安装成功。常见报错之版本选择问题 第一步&#xff1a;在线环境下载离线安装包及依赖包。 下载第三方库官网&am…

苍穹外卖项目DAY01

苍穹外卖项目Day01 1、软件开发整体介绍 1.1、软件开发流程 1.2、角色分工 项目经理&#xff1a;对整个项目负责&#xff0c;任务分配、把控进度产品经理&#xff1a;进行需求调研&#xff0c;输出需求调研文档、产品原型等UI设计师&#xff1a;根据产品原型输出界面效果图架…

vue-cli(五)

中央事件总线bus挂载原型链上 首先&#xff0c;在main.js中对Vue实例进行挂载 new Vue({render: h > h(App),router,//在创建前指向busbeforeCreate() {Vue.prototype.$bus this;},}).$mount(#app) 第二&#xff1a;修改FirstBus中传参的方式 通过$emit的方式给其他组件…

Ubuntu 下 通过 Docker 部署 Vaultwarden 服务器

今天&#xff0c;我在三丰云这个不错的免费云服务器上进行 Vaultwarden 部署测试。这个免费云服务器性能不错&#xff0c;1核CPU、1G内存、10G硬盘、5M带宽&#xff0c;完全满足我的需求。三丰云的免费服务器确实不错&#xff0c;让我这个资深的Fivver卖家可以在上面尽情发挥我…

todesk传输文件损坏问题(压缩包损坏)

文章目录 问题描述 问题描述 todesk传输文件貌似有点bug&#xff0c;传之前文件好好的&#xff0c;传过去就不行了&#xff0c;有时是压缩包无法解压&#xff0c;有时是能解压但是里面文件有的是好的&#xff0c;有的是坏的。 用企业微信、qq、time、向日葵传都没有问题&…

学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术

目录 一、概述与基础概念 1.1 套接字&#xff08;Socket&#xff09;概念 1.2 底层原理与网络协议 1.2.1 网络协议 1.2.2 套接字工作原理 二、C套接字编程核心技术 2.1 套接字编程的基本步骤 2.2 套接字编程详细实现 2.2.1 创建套接字 2.2.2 绑定地址 2.2.3 监听和接…

为Linux/centos虚拟机已有硬盘扩容

为Linux已有硬盘扩容 旧盘扩容的大体流程与关键命令&#xff1a; 为虚拟机磁盘加容量&#xff1b; 为磁盘新容量分区&#xff08;fdisk&#xff09;&#xff1b; 将分区信息写入内核&#xff08;partx&#xff09;&#xff1b; 为分区创建物理卷&#xff08;pvcreate&#xff0…

8-11章节复习总结

文章目录 数据库技术三级模式两级映射数据设计数据模型E-R模型关系模型练习题 关系代数关系代数运算符练习题 SQL语句练习题 数据库控制练习题 数据库故障和备份函数依赖函数依赖的公理系统键与约束 规范化第一范式1NF第二范式2NF第三范式3NF练习题 模式分解练习题 标准化与知识…

python工具--mysql2doris的datax json生成工具

一、说明 要做大量的datax来同步mysql-doris&#xff0c;会需要写很多datax的json文件&#xff0c;为了省事&#xff0c;写了工具&#xff0c;只要提供doris的建表语句即可生产json。 二、文件说明 一共用到了五个文档 2.1 conf.json 这里是Mysql和doris的链接信息 其中 t…

【LeetCode面试150】——209长度最小的子数组

博客昵称&#xff1a;沈小农学编程 作者简介&#xff1a;一名在读硕士&#xff0c;定期更新相关算法面试题&#xff0c;欢迎关注小弟&#xff01; PS&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟沈小农&#xff0c;希望我的文章能帮助到你。欢迎大家在…

Unity | AmplifyShaderEditor插件基础(第二集:模版说明)

目录 一、前言 二、核心模版和URP模版 1.区别介绍 2.自己的模版 三、输出节点 1.界面 2.打开OutPut 3.ShderType 4.ShaderName 5.Shader大块内容 6.修改内容 四、预告 一、前言 内容全部基于以下链接基础以上讲的。 Unity | Shader基础知识&#xff08;什么是shader…

如何用GPT写一本小说:详细教程

想写一本小说&#xff0c;却不知道从哪里开始&#xff1f;现在有了GPT这样的AI工具&#xff0c;这个过程可以变得更加轻松和有趣。GPT不仅能帮助你构思故事&#xff0c;还能为你提供灵感、写作指导&#xff0c;甚至是完成整本小说。在这个教程中&#xff0c;详细讲解如何使用GP…

vscode 远程控制ssh操作步骤

1.下载拓展Remote - SSH 打开Visual Studio Code&#xff0c;进入拓展市场(CtrlShiftX)&#xff0c;下载拓展Remote - SSH 2.选择远程(隧道/SSH)类别 点击远程资源管理器选项卡&#xff0c;并选择远程(隧道/SSH)类别 4.在弹出的选择配置文件中&#xff0c;点击第一个 5. 连接到…

解决 idea 创建maven项目卡住

一, 现象 选择一个Archetype后创建项目,一直卡着,点哪里都点不了,有的博客说可以看maven的日志排查问题,我这里没有任何日志输出 二,为什么会卡住 结论: 因为idea在从中央仓库下载archetype-catalog.xml(文件较大,14.8M)导致卡住 分析: 首先要明白通过Archetype创建…

乾坤qiankun搭建前端微服务

本教程适合于qiankun新手&#xff0c;手把手教你搭建一个简单的本地前端微服务&#xff0c;附完整代码 一、什么是微前端 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。 微前端架构核心价值&#xff1a; 技术栈无关&#xff1…