BIO、NIO 和 AIO 基础介绍

BIO、NIO 和 AIO 基础介绍

在 Java 网络编程中,BIO、NIO、AIO 是三种核心的 I/O 模型,分别对应不同的阻塞/非阻塞、同步/异步特性,适用于不同的业务场景。

一、核心概念

1.1 基本定义

  • BIO:同步阻塞 I/O(Blocking I/O)
  • NIO:同步非阻塞 I/O(Non-blocking I/O),也叫 New I/O,核心是「多路复用」
  • AIO:异步非阻塞 I/O(Asynchronous I/O),也叫 NIO 2.0

1.2 重要概念区分

同步/异步 关注的是「是否需要等待 I/O 操作完成」

  • 同步:线程主动等待 I/O 结果,直到操作完成
  • 异步:线程无需等待,I/O 完成后由系统回调通知

阻塞/非阻塞 关注的是「等待时线程是否被挂起」

  • 阻塞:线程在 I/O 过程中被挂起,无法做其他事
  • 非阻塞:线程在 I/O 过程中不挂起,可继续执行其他逻辑

1.3 三种模型对比速览

模型 同步/异步 阻塞/非阻塞 线程模型 适用场景
BIO 同步 阻塞 一连接一线程 连接数少、低并发
NIO 同步 非阻塞 单线程处理多连接 高并发、短连接
AIO 异步 非阻塞 回调机制、无需轮询 高并发、长耗时 I/O

二、BIO(同步阻塞 I/O)

2.1 核心特点

  • 工作原理:线程发起 I/O 请求后,必须阻塞等待 I/O 操作完成(如读取数据、写入数据),期间线程无法做任何事;
  • 线程模型:每处理一个连接需要一个独立线程,连接数多的时候会导致线程数暴涨,性能瓶颈明显;
  • 编程模型:简单直观,适合连接数少、单次操作耗时短的场景(如简单的客户端-服务器通信)。

2.2 BIO 工作流程图

客户端1
服务器主线程
客户端2
客户端3
线程1
线程2
线程3
阻塞等待数据
阻塞等待数据
阻塞等待数据
处理数据
处理数据
处理数据

2.3 代码示例

(1)BIO 服务器
java 复制代码
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * BIO 同步阻塞服务器示例(优化后:使用线程池)
 * 每一个客户端连接对应一个线程,线程阻塞等待数据读取
 */
public class BioServer {
    private static final int PORT = 8080;
    private static final int MAX_THREADS = 100;
    
    public static void main(String[] args) throws IOException {
        // 使用线程池限制线程数量,避免无限创建线程
        ExecutorService threadPool = Executors.newFixedThreadPool(MAX_THREADS);
        
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("BIO 服务器已启动,监听端口 " + PORT + "...");
            System.out.println("最大并发线程数:" + MAX_THREADS);

            while (true) {
                // 阻塞点1:accept() 阻塞直到有客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("新客户端连接:" + socket.getInetAddress() + 
                                 ":" + socket.getPort());

                // 将连接提交给线程池处理
                threadPool.submit(new ClientHandler(socket));
            }
        }
    }
    
    /**
     * 客户端处理器(每个连接一个任务)
     */
    static class ClientHandler implements Runnable {
        private final Socket socket;
        
        public ClientHandler(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            try (InputStream inputStream = socket.getInputStream()) {
                byte[] buffer = new byte[1024];
                while (true) {
                    // 阻塞点2:read() 阻塞直到有数据可读
                    int len = inputStream.read(buffer);
                    if (len == -1) { // 客户端关闭连接
                        System.out.println("客户端断开连接:" + 
                                         socket.getInetAddress() + ":" + socket.getPort());
                        break;
                    }
                    String msg = new String(buffer, 0, len);
                    System.out.println("[" + Thread.currentThread().getName() + 
                                     "] 收到消息:" + msg);
                    
                    // TODO: 业务处理
                    
                    // 模拟处理耗时
                    Thread.sleep(100);
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
(2)BIO 客户端
java 复制代码
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

/**
 * BIO 客户端示例
 */
public class BioClient {
    private static final String SERVER_HOST = "localhost";
    private static final int SERVER_PORT = 8080;
    
    public static void main(String[] args) {
        try (Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
             OutputStream outputStream = socket.getOutputStream();
             Scanner scanner = new Scanner(System.in)) {
            
            System.out.println("已连接到服务器 " + SERVER_HOST + ":" + SERVER_PORT);
            System.out.println("请输入要发送的消息(输入 'exit' 退出):");
            
            while (true) {
                System.out.print("> ");
                String msg = scanner.nextLine();
                
                if ("exit".equalsIgnoreCase(msg)) {
                    break;
                }
                
                // 发送数据(阻塞,直到数据写入完成)
                outputStream.write(msg.getBytes());
                outputStream.flush();
                System.out.println("消息已发送");
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("客户端已关闭");
    }
}

2.4 BIO 优缺点

优点 缺点
编程模型简单,易于理解和实现 线程资源消耗大,连接数多时性能急剧下降
代码调试方便,执行流程清晰 线程阻塞造成资源浪费,CPU 利用率低
适合低并发、小数据量场景 系统稳定性差,容易因线程过多导致宕机

三、NIO(同步非阻塞 I/O)

3.1 核心特点

  • 工作原理:线程发起 I/O 请求后,无需阻塞等待,可先去处理其他任务,定期轮询查看 I/O 是否完成;
  • 核心组件
    • Channel(通道):双向读写,替代 BIO 的 Stream
    • Buffer(缓冲区):数据读写的载体
    • Selector(多路复用器):核心组件,单线程监听多个 Channel 的事件
  • 多路复用:一个 Selector 可以监听多个 Channel 的事件(连接、读、写),单个线程就能处理大量连接,解决 BIO 线程膨胀问题;
  • 适用场景:连接数多、单次操作耗时短的场景(如高并发的 Web 服务器)。

3.2 NIO 多路复用核心流程图



ACCEPT(新连接)
READ(可读)


WRITE(可写)


创建 ServerSocketChannel
配置为非阻塞模式
绑定监听端口
创建 Selector(多路复用器)
将 ServerSocketChannel 注册到 Selector,关注 ACCEPT 事件
Selector 轮询就绪事件 select()
是否有就绪事件?
获取就绪的 SelectionKey 集合
遍历 SelectionKey 集合
判断事件类型
通过 ServerSocketChannel 接收 SocketChannel
配置 SocketChannel 为非阻塞模式
将 SocketChannel 注册到 Selector,关注 READ 事件
通过 SocketChannel 读取数据到 Buffer
处理业务逻辑
是否需要返回数据?
修改关注事件为 WRITE
通过 SocketChannel 写入 Buffer 数据
数据写入完成?
修改关注事件为 READ

3.3 NIO 代码示例

java 复制代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * NIO 多路复用服务器示例(完整版)
 * 单个线程通过 Selector 处理所有客户端连接和读写事件
 */
public class NioServer {
    private static final int PORT = 8080;
    private static final int BUFFER_SIZE = 1024;
    private static final long SELECTOR_TIMEOUT = 1000; // 1秒超时
    
    // 统计在线客户端数
    private static final ConcurrentHashMap<SocketChannel, String> clients = 
        new ConcurrentHashMap<>();
    
    public static void main(String[] args) {
        try {
            new NioServer().start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void start() throws IOException {
        // 1. 创建 ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        
        // 2. 配置为非阻塞模式
        serverSocketChannel.configureBlocking(false);
        
        // 3. 绑定端口
        serverSocketChannel.bind(new InetSocketAddress(PORT));
        System.out.println("NIO 服务器已启动,监听端口 " + PORT + "...");
        
        // 4. 创建 Selector
        Selector selector = Selector.open();
        
        // 5. 注册 ServerSocketChannel 到 Selector,关注 ACCEPT 事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        // 6. 主循环:处理就绪事件
        while (true) {
            // 阻塞等待就绪事件(带超时)
            int readyChannels = selector.select(SELECTOR_TIMEOUT);
            
            if (readyChannels == 0) {
                continue; // 无就绪事件,继续轮询
            }
            
            // 获取所有就绪的 SelectionKey
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            
            // 遍历处理每个就绪事件
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove(); // 必须移除,避免重复处理
                
                try {
                    // 处理事件
                    if (!key.isValid()) {
                        continue;
                    }
                    
                    if (key.isAcceptable()) {
                        handleAccept(key, selector);
                    } else if (key.isReadable()) {
                        handleRead(key);
                    } else if (key.isWritable()) {
                        handleWrite(key);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    // 异常时关闭连接
                    key.cancel();
                    closeChannel(key.channel());
                }
            }
            
            // 可选:打印在线客户端数
            System.out.println("当前在线客户端数:" + clients.size());
        }
    }
    
    /**
     * 处理新连接
     */
    private void handleAccept(SelectionKey key, Selector selector) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        
        // 接收新连接(非阻塞)
        SocketChannel socketChannel = serverChannel.accept();
        if (socketChannel == null) return;
        
        // 配置为非阻塞模式
        socketChannel.configureBlocking(false);
        
        // 注册 READ 事件
        socketChannel.register(selector, SelectionKey.OP_READ, 
                              ByteBuffer.allocate(BUFFER_SIZE));
        
        // 记录客户端
        String clientInfo = socketChannel.getRemoteAddress().toString();
        clients.put(socketChannel, clientInfo);
        System.out.println("新客户端连接:" + clientInfo);
        System.out.println("当前在线客户端数:" + clients.size());
    }
    
    /**
     * 处理读事件
     */
    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        
        // 读取数据(非阻塞)
        int bytesRead = socketChannel.read(buffer);
        
        if (bytesRead == -1) {
            // 客户端关闭连接
            String clientInfo = clients.remove(socketChannel);
            System.out.println("客户端断开连接:" + clientInfo);
            key.cancel();
            closeChannel(socketChannel);
            return;
        }
        
        if (bytesRead > 0) {
            // 切换到读模式
            buffer.flip();
            
            // 读取数据
            byte[] data = new byte[buffer.limit()];
            buffer.get(data);
            String message = new String(data).trim();
            
            // 获取客户端信息
            String clientInfo = clients.get(socketChannel);
            System.out.println("收到来自 " + clientInfo + " 的消息:" + message);
            
            // 清空缓冲区,准备下次读取
            buffer.clear();
            
            // 准备响应数据
            String response = "服务器已收到: " + message;
            ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
            
            // 如果需要返回数据,修改关注事件为 WRITE
            key.interestOps(SelectionKey.OP_WRITE);
            key.attach(responseBuffer);
        }
    }
    
    /**
     * 处理写事件
     */
    private void handleWrite(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        
        // 写入数据(非阻塞)
        socketChannel.write(buffer);
        
        if (!buffer.hasRemaining()) {
            // 数据写入完成,重新关注 READ 事件
            key.interestOps(SelectionKey.OP_READ);
            key.attach(ByteBuffer.allocate(BUFFER_SIZE));
        }
    }
    
    /**
     * 关闭通道
     */
    private void closeChannel(Channel channel) {
        if (channel != null && channel.isOpen()) {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.4 NIO 优缺点

优点 缺点
单线程可处理大量连接,资源消耗小 编程复杂度高,需要处理各种边界情况
高并发下性能优秀,CPU 利用率高 调试会比较困难,容易出现 bug
数据读写灵活,可精确控制缓冲区 需要处理半包/粘包问题

四、AIO(异步非阻塞 I/O)

4.1 核心特点

  • 工作原理:线程发起 I/O 请求后,直接返回,无需轮询,I/O 操作由操作系统完成后,通过回调函数通知线程处理结果;
  • 核心组件
    • AsynchronousServerSocketChannel:异步服务器通道
    • AsynchronousSocketChannel:异步客户端通道
    • CompletionHandler:回调处理器
  • 优势:无需手动多路复用,由系统底层完成,编程模型更简洁;
  • 适用场景:连接数多、单次操作耗时长的场景(如文件下载、大数据传输)。

4.2 AIO 工作流程图

回调处理器 操作系统内核 应用程序 回调处理器 操作系统内核 应用程序 立即返回,不阻塞 整个过程应用程序无需阻塞等待 1. 发起异步读操作 2. 继续执行其他任务 3. 内核准备数据 4. 内核将数据拷贝到缓冲区 5. I/O 完成,触发回调 6. 处理数据

4.3 AIO 代码示例

java 复制代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * AIO 异步非阻塞服务器示例(完整版)
 * 基于回调处理连接和读写事件,无需轮询
 */
public class AioServer {
    private static final int PORT = 8080;
    private static final int BUFFER_SIZE = 1024;
    private static final ConcurrentHashMap<AsynchronousSocketChannel, String> clients = 
        new ConcurrentHashMap<>();
    
    // 用于处理回调的业务线程池
    private ExecutorService businessThreadPool = Executors.newFixedThreadPool(10);
    
    public static void main(String[] args) {
        try {
            new AioServer().start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void start() throws IOException {
        // 1. 创建异步服务器通道
        AsynchronousServerSocketChannel serverChannel = 
            AsynchronousServerSocketChannel.open();
        
        // 2. 绑定端口
        serverChannel.bind(new InetSocketAddress(PORT));
        System.out.println("AIO 服务器已启动,监听端口 " + PORT + "...");
        
        // 3. 异步接收连接
        serverChannel.accept(null, new AcceptCompletionHandler(serverChannel));
        
        // 4. 防止主线程退出
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            businessThreadPool.shutdown();
        }
    }
    
    /**
     * 连接接收回调处理器
     */
    class AcceptCompletionHandler implements 
            CompletionHandler<AsynchronousSocketChannel, Void> {
        
        private final AsynchronousServerSocketChannel serverChannel;
        
        public AcceptCompletionHandler(AsynchronousServerSocketChannel serverChannel) {
            this.serverChannel = serverChannel;
        }
        
        @Override
        public void completed(AsynchronousSocketChannel socketChannel, Void attachment) {
            // 立即再次接收新连接(关键:必须再次调用accept,形成循环)
            serverChannel.accept(null, this);
            
            try {
                // 处理新连接
                String clientInfo = socketChannel.getRemoteAddress().toString();
                clients.put(socketChannel, clientInfo);
                System.out.println("新客户端连接:" + clientInfo);
                System.out.println("当前在线客户端数:" + clients.size());
                
                // 创建缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
                
                // 异步读取数据
                socketChannel.read(buffer, buffer, new ReadCompletionHandler(socketChannel));
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        @Override
        public void failed(Throwable exc, Void attachment) {
            System.err.println("接收连接失败:" + exc.getMessage());
            // 继续接收(失败后重试)
            serverChannel.accept(null, this);
        }
    }
    
    /**
     * 读取数据回调处理器
     */
    class ReadCompletionHandler implements 
            CompletionHandler<Integer, ByteBuffer> {
        
        private final AsynchronousSocketChannel socketChannel;
        
        public ReadCompletionHandler(AsynchronousSocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }
        
        @Override
        public void completed(Integer bytesRead, ByteBuffer buffer) {
            if (bytesRead == -1) {
                // 客户端关闭连接
                String clientInfo = clients.remove(socketChannel);
                System.out.println("客户端断开连接:" + clientInfo);
                try {
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return;
            }
            
            if (bytesRead > 0) {
                // 切换到读模式
                buffer.flip();
                
                // 读取数据
                byte[] data = new byte[buffer.limit()];
                buffer.get(data);
                String message = new String(data).trim();
                
                // 获取客户端信息
                String clientInfo = clients.get(socketChannel);
                
                // 业务处理(提交到业务线程池,避免阻塞回调线程)
                businessThreadPool.submit(() -> {
                    try {
                        System.out.println("收到来自 " + clientInfo + " 的消息:" + message);
                        
                        // 模拟业务处理
                        String processedMsg = processMessage(message);
                        
                        // 准备响应
                        String response = "服务器响应: " + processedMsg;
                        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
                        
                        // 异步写入响应
                        socketChannel.write(responseBuffer, null, 
                            new WriteCompletionHandler(socketChannel, buffer));
                        
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
        }
        
        @Override
        public void failed(Throwable exc, ByteBuffer buffer) {
            System.err.println("读取数据失败:" + exc.getMessage());
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        /**
         * 模拟消息处理
         */
        private String processMessage(String message) {
            // TODO: 实际的业务处理
            return "processed: " + message;
        }
    }
    
    /**
     * 写入数据回调处理器
     */
    class WriteCompletionHandler implements 
            CompletionHandler<Integer, ByteBuffer> {
        
        private final AsynchronousSocketChannel socketChannel;
        private final ByteBuffer buffer;
        
        public WriteCompletionHandler(AsynchronousSocketChannel socketChannel, 
                                     ByteBuffer buffer) {
            this.socketChannel = socketChannel;
            this.buffer = buffer;
        }
        
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            // 数据写入完成后,清空缓冲区并继续读取下一条消息
            buffer.clear();
            socketChannel.read(buffer, buffer, new ReadCompletionHandler(socketChannel));
        }
        
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            System.err.println("写入数据失败:" + exc.getMessage());
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4.4 AIO 优缺点

优点 缺点
真正的异步 I/O,无需轮询 编程复杂度高,回调嵌套容易造成混乱
适合长连接、大文件传输 JDK 对 AIO 的支持不如 NIO 成熟
系统资源利用率高 调试困难,错误处理复杂

五、三种 I/O 模型深度对比

5.1 数据流对比图

AIO 流程
发起异步读请求
立即返回
继续其他任务
内核准备数据并拷贝
回调通知
处理数据
NIO 流程


发起读请求
数据是否就绪?
立即返回

继续其他任务
轮询检查
阻塞等待数据拷贝
处理数据
BIO 流程
发起读请求
阻塞等待内核准备数据
阻塞等待内核拷贝数据
处理数据

5.2 详细对比表

对比维度 BIO NIO AIO
I/O 模型 同步阻塞 同步非阻塞 异步非阻塞
线程模型 一个连接一个线程 一个线程处理多个连接 回调机制,不占用线程
数据拷贝 应用线程负责 应用线程负责 内核完成,回调通知
编程复杂度 简单 中等 复杂
调试难度 容易 中等 困难
CPU 利用率 低(线程阻塞)
内存占用 高(线程多)
并发能力
吞吐量
响应时间 中等
典型应用 传统 Socket,JDBC Tomcat,Netty,Jetty 文件服务器,专业中间件
Java 版本 JDK 1.0+ JDK 1.4+ JDK 1.7+

5.3 性能对比场景

场景 BIO NIO AIO 推荐方案
连接数 < 1000 较好 BIO(简单)
连接数 1000-10000 NIO
连接数 > 10000 不可用 较好 AIO
短连接、高频请求 优秀 NIO
长连接、低频交互 优秀 AIO
大文件传输 中等 优秀 AIO
实时性要求高 中等 优秀 优秀 NIO/AIO

六、实际应用建议

6.1 技术选型指南

  1. 简单应用,连接数少(<100):

    • 选择 BIO,开发简单,维护容易
    • 示例:小型管理系统、内部工具
  2. 高并发 Web 服务(1000-10000 连接):

    • 选择 NIO,性能好,生态丰富
    • 示例:Web 服务器、API 网关、即时通讯
  3. 大文件传输、长连接

    • 选择 AIO,真正的异步 I/O
    • 示例:文件服务器、视频流媒体
  4. 中间件开发

    • 选择 NIO(如 Netty),稳定性好,社区活跃
    • 示例:RPC 框架、消息队列
  • 大多数场景:选择 NIO(通过 Netty 等框架)
  • 简单场景:BIO 足够
  • 特殊场景(大文件、长连接):考虑 AIO

6.2 常见框架对比

框架 底层 I/O 模型 适用场景
Tomcat NIO(默认)/BIO Web 应用服务器
Netty NIO 高性能网络应用
Jetty NIO 嵌入式 Web 服务器
Undertow NIO 高并发 Web 服务器
Grizzly NIO 通用网络框架

6.3 最佳实践

  1. 避免在 NIO 的 Handler 中做耗时操作,应提交到业务线程池
  2. 合理设置缓冲区大小,避免内存浪费
  3. 处理半包/粘包问题,使用消息定长或分隔符,发送消息的时候先发送数据长度,再发送消息,根据消息长度再获取消息
  4. 做好异常处理,避免资源泄露
  5. 监控线程池和连接数,及时发现问题

相关推荐
专注VB编程开发20年1 小时前
深思数盾国产.NET 加密工具与 VMProtect、.NET Reactor、Zprotect、ILProtector 的优缺点
大数据·网络·.net·加密·加壳
Xzq2105091 小时前
网络基础学习(一)
网络·学习
爱丽_1 小时前
WebSocket/Netty 实时通信:从连接管理到消息路由
网络·websocket·网络协议
Lsir10110_1 小时前
【Linux】网络基础——协议与网络传输基本原理
运维·服务器·网络
路由侠内网穿透.2 小时前
本地部署中间件系统 JBoss 并实现外部访问
运维·服务器·网络·网络协议·中间件
嵌入小生0072 小时前
网络通信 --- TCP并发服务器/IO模型/多路复用IO相关函数接口 --- Linux
服务器·网络·select·tcp并发服务器·fcntl·io模型·多路复用io
大母猴啃编程2 小时前
Socket编程UDP
linux·网络·c++·网络协议·udp
kessy12 小时前
LKT4304加密芯片在工业PLC控制器中的安全应用案例
网络
艾莉丝努力练剑2 小时前
MySQL查看命令速查表
linux·运维·服务器·网络·数据库·人工智能·mysql