BIO和NIO编程(待完善)

目录

IO模型

BIO

NIO

常见问题


IO模型

Java共支持3种网络编程IO模式:BIO,NIO,AIO

BIO

同步阻塞模型,一个客户端连接对应一个处理线程

代码示例:

Server端:

java 复制代码
public class BioServer {

    private static ExecutorService executorService = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器启动成功");
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println("客户端连接成功");
            executorService.execute(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
                    String message;
                    while ((message = reader.readLine()) != null) {
                        System.out.println("收到消息: " + message);
                        writer.write("已收到消息: " + message);
                        writer.newLine();
                        writer.flush();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

Client端:

java 复制代码
public class BioClient {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
        String message;
        while ((message = consoleReader.readLine()) != null) {
            writer.write(message);
            writer.newLine();
            writer.flush();
            System.out.println("发送消息: " + message);
            System.out.println("收到回复: " + reader.readLine());
        }
        socket.close();
    }
}

执行结果:

存在问题:

  • IO操作是阻塞操作, 连接时读写会造成线程阻塞
  • 线程太多会造成CPU异常

应用场景:

BIO 方式适用连接数较小且固定的架构, 这种方式对服务器资源要求比较高, 但代码简单易理解。

NIO

同步非阻塞,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理,JDK1.4开始引入。

应用场景:

NIO方式适用于连接数目多且连接比较短(轻操作) 的架构, 如聊天服务器, 弹幕系统, 服务器间通讯,编程比较复杂。

代码

java 复制代码
public class BioSelectorServer {

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(9000));
        // 设置非阻塞
        serverSocket.configureBlocking(false);
        // 创建多路复用器
        Selector selector = Selector.open();
        // 注册serversocket到selector, 关注连接事件
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务启动成功");
        while (true) {
            // 阻塞等待需要处理的事件
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 如果是连接事件  > 获取连接并且注册读事件
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("客户端连接成功");
                } else if (key.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int len = socketChannel.read(byteBuffer);
                    if (len > 0) {
                        System.out.println("接收到消息:" + new String(byteBuffer.array()));
                    } else {
                        System.out.println("客户端断开连接");
                        socketChannel.close();
                    }
                }
                // 从事件集合里删除本次处理的key,防止下次select重复处理
                iterator.remove();
            }
        }
    }
}

NIO的三大核心组件:

  1. channel 类似于流,每个 channel 对应一个 buffer缓冲区,buffer 底层就是个数组
  2. channel 会注册到 selector 上,由 selector 根据 channel 读写事件的发生将其交由某个空闲的线程处理
  3. NIO 的 Buffer 和 channel 都是既可以读也可以写

常见问题

为什么Netty使用NIO而不是AIO?

在Linux系统上,AIO的底层实现仍使用Epoll,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层,不容易深度优化,Linux上AIO还不够成熟。Netty是异步非阻塞框架,Netty在NIO上做了很多异步的封装。

相关推荐
西瓜er6 小时前
JAVA:Spring Boot 集成 FFmpeg 实现多媒体处理
java·spring boot·ffmpeg
你总是一副不开心的样子(´ . .̫ .7 小时前
一、十天速通Java面试(第三天)
java·面试·职场和发展·java面试
迎風吹頭髮7 小时前
UNIX下C语言编程与实践63-UNIX 并发 Socket 编程:非阻塞套接字与轮询模型
java·c语言·unix
我是华为OD~HR~栗栗呀7 小时前
23届考研-Java面经(华为OD)
java·c++·python·华为od·华为·面试
Javatutouhouduan7 小时前
Java程序员如何深入学习JVM底层原理?
java·jvm·java面试·后端开发·java架构师·java程序员·互联网大厂
王嘉俊9257 小时前
设计模式--享元模式:优化内存使用的轻量级设计
java·设计模式·享元模式
2301_803554528 小时前
C++联合体(Union)详解:与结构体的区别、联系与深度解析
java·c++·算法
EnCi Zheng8 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印6018 小时前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring
为什么我不是源代码8 小时前
JPA读取数据库离谱问题-No property ‘selectClassByName‘ found-Not a managed type
java·sql