BIO、NIO和AIO

BIO、NIO和AIO是Java网络编程中的三种主要IO模型。

  1. BIO(Blocking IO):同步并阻塞,服务实现模式为一个连接对应一个线程,即客户端发送一个连接,服务端要有一个线程来处理。如果连接多了,线程数量不够,就只能等待,即会发生阻塞。BIO适用于连接数目比较小且固定的架构,对服务器要求比较高,并发局限于应用中。
  2. NIO(Non-Blocking IO):同步非阻塞,服务实现模式是一个线程可以处理多个连接,即客户端发送的连接都会注册到多路复用器上,然后进行轮询连接,有I/O请求就处理。NIO适用于连接数目多且连接比较短的架构,如聊天服务器、弹幕系统等,编程比较复杂。NIO以I/O块(buffer)的形式处理数据,效率比BIO的I/O流更高。NIO是面向缓冲区或面向块编程的,支持双向操作,而BIO则是单向的,要么是输入流要么是输出流。
  3. AIO(Asynchronous IO):异步非阻塞,引入了异步通道,采用的是proactor模式,特点是有效的请求才启动线程,先有操作系统完成再通知服务端。AIO适用于连接数目多且连接长的架构,如相册服务器。AIO是基于事件和回调机制进行异步IO操作,不需要线程主动去查询IO事件,当有数据可读时会通知给线程。

总的来说,三种IO模型各有优缺点,适用于不同的场景和需求。在选择合适的IO模型时,需要考虑并发量、连接时长、服务器资源等因素。

应用场景:

BIO(Blocking IO,阻塞IO):

  • 应用场景:适用于连接数比较小且固定的架构,例如早期的Web服务器。这种方式对服务器资源要求较高,因为每个连接都需要一个单独的线程。在JDK 1.4之前的版本中,这是唯一的IO模型选择。
  • 优点:程序直观、简单易理解。
  • 缺点:并发局限于应用中,当连接数增多时,需要更多的线程来处理,这可能导致系统资源耗尽。

NIO(Non-Blocking IO,非阻塞IO):

  • 应用场景:适用于连接数比较多且连接比较短的架构,如聊天服务器、网络聊天应用等。这种模型对编程的要求比较高,从JDK 1.4开始支持。
  • 优点:通过多路复用器(Selector)可以同时处理多个连接,提高了系统的并发处理能力。
  • 缺点:虽然解决了BIO中并发量的问题,但仍然需要轮询检查,因此CPU资源可能会有所浪费。

AIO(Asynchronous IO,异步IO):

  • 应用场景:适用于连接数比较多且连接比较长的架构,如相册服务器、大文件传输等。这种模型充分利用了操作系统的参与,对编程的要求也很高,从JDK 7开始支持。
  • 优点:实现了真正的异步非阻塞IO操作,当数据准备好时,操作系统会通知应用程序,从而提高了系统的响应速度和效率。
  • 缺点:编程复杂,需要深入理解异步编程模型和操作系统的支持。

下面是一个简单的Java代码示例,展示了如何使用BIO(Blocking I/O)模型创建一个基本的服务器和客户端。这个例子中的服务器使用`ServerSocket`来监听客户端连接,并为每个连接创建一个新的线程。客户端使用`Socket`来连接到服务器,并发送和接收数据。

BIO例子

**服务器端(Server.java)**:

import java.io.*;  
import java.net.ServerSocket;  
import java.net.Socket;  
  
public class Server {  
  
    public static void main(String[] args) throws IOException {  
        int port = 8080;  
        ServerSocket serverSocket = new ServerSocket(port);  
  
        System.out.println("Server started on port: " + port);  
  
        while (true) {  
            Socket clientSocket = serverSocket.accept(); // 阻塞等待客户端连接  
            System.out.println("Accepted connection from " + clientSocket.getRemoteSocketAddress());  
  
            new Thread(new ClientHandler(clientSocket)).start(); // 为每个客户端连接创建新线程  
        }  
    }  
  
    static class ClientHandler implements Runnable {  
        private final Socket socket;  
  
        public ClientHandler(Socket socket) {  
            this.socket = socket;  
        }  
  
        @Override  
        public void run() {  
            try (  
                    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
                    PrintWriter out = new PrintWriter(socket.getOutputStream(), true)  
            ) {  
                String inputLine;  
                while ((inputLine = in.readLine()) != null) {  
                    System.out.println("Received from client: " + inputLine);  
                    out.println("Server response: " + inputLine);  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            } finally {  
                try {  
                    socket.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}

**客户端(Client.java)**:

import java.io.*;  
import java.net.Socket;  
  
public class Client {  
  
    public static void main(String[] args) throws IOException {  
        String hostname = "localhost";  
        int port = 8080;  
  
        try (  
                Socket socket = new Socket(hostname, port);  
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);  
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
                BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))  
        ) {  
            System.out.println("Connected to server");  
  
            String userInput;  
            while ((userInput = stdIn.readLine()) != null) {  
                out.println(userInput); // 发送数据到服务器  
                System.out.println("Sent to server: " + userInput);  
  
                String response = in.readLine(); // 接收来自服务器的数据  
                System.out.println("Received from server: " + response);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}

NIO例子

**服务器端(NioServer.java)**:

import java.io.IOException;  
import java.net.InetSocketAddress;  
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.util.Iterator;  
import java.util.Set;  
  
public class NioServer {  
  
    public static void main(String[] args) throws IOException {  
        int port = 8080;  
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  
        serverSocketChannel.configureBlocking(false); // 设置为非阻塞模式  
        serverSocketChannel.socket().bind(new InetSocketAddress(port));  
  
        Selector selector = Selector.open();  
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册OP_ACCEPT事件  
  
        while (true) {  
            int readyChannels = selector.select(); // 阻塞等待就绪的Channel  
            if (readyChannels == 0) continue;  
  
            Set<SelectionKey> selectedKeys = selector.selectedKeys();  
            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();  
  
            while (keyIterator.hasNext()) {  
                SelectionKey key = keyIterator.next();  
  
                if (key.isAcceptable()) { // 有新的连接  
                    SocketChannel client = serverSocketChannel.accept();  
                    client.configureBlocking(false);  
                    client.register(selector, SelectionKey.OP_READ);  
                    System.out.println("Accepted connection from " + client);  
                } else if (key.isReadable()) { // 可读  
                    SocketChannel client = (SocketChannel) key.channel();  
                    ByteBuffer buffer = ByteBuffer.allocate(1024);  
                    int bytesRead = client.read(buffer);  
                    if (bytesRead == -1) {  
                        client.close();  
                    } else {  
                        buffer.flip();  
                        while (buffer.hasRemaining()) {  
                            System.out.print((char) buffer.get());  
                        }  
                        client.register(selector, SelectionKey.OP_WRITE);  
                    }  
                } else if (key.isWritable()) { // 可写  
                    SocketChannel client = (SocketChannel) key.channel();  
                    ByteBuffer buffer = ByteBuffer.allocate(1024);  
                    buffer.put("Hello from server!".getBytes());  
                    buffer.flip();  
                    client.write(buffer);  
                    client.close();  
                }  
  
                keyIterator.remove();  
            }  
        }  
    }  
}

**客户端(NioClient.java)**:

import java.io.IOException;  
import java.net.InetSocketAddress;  
import java.nio.ByteBuffer;  
import java.nio.channels.SocketChannel;  
  
public class NioClient {  
  
    public static void main(String[] args) {  
        String host = "localhost";  
        int port = 8080;  
  
        try (SocketChannel clientChannel = SocketChannel.open()) {  
            clientChannel.configureBlocking(false); // 设置为非阻塞模式  
  
            // 异步连接服务器  
            clientChannel.connect(new InetSocketAddress(host, port));  
  
            // 等待连接完成  
            while (!clientChannel.finishConnect()) {  
                // 连接尚未完成,可以继续做其他事情  
                System.out.println("Connecting to server...");  
            }  
  
            System.out.println("Connected to server");  
  
            // 准备发送的数据  
            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);  
            writeBuffer.put("Hello from improved client!".getBytes());  
            writeBuffer.flip();  
  
            // 发送数据  
            while (writeBuffer.hasRemaining()) {  
                clientChannel.write(writeBuffer);  
            }  
  
            // 准备接收服务器响应的缓冲区  
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);  
  
            // 等待并读取服务器的响应  
            while (true) {  
                int bytesRead = clientChannel.read(readBuffer);  
                if (bytesRead == -1) {  
                    // 连接已关闭或没有数据可读  
                    break;  
                }  
  
                // 反转缓冲区,准备读取数据  
                readBuffer.flip();  
  
                // 处理接收到的数据  
                while (readBuffer.hasRemaining()) {  
                    System.out.print((char) readBuffer.get());  
                }  
  
                // 清除缓冲区,为下一次读取准备  
                readBuffer.clear();  
            }  
  
            // 关闭连接  
            clientChannel.close();  
  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}

AIO例子

import java.io.IOException;  
import java.nio.ByteBuffer;  
import java.nio.channels.AsynchronousFileChannel;  
import java.nio.channels.CompletionHandler;  
import java.nio.file.Paths;  
import java.nio.file.StandardOpenOption;  
  
public class AIOExample {  
  
    public static void main(String[] args) {  
        String filePath = "path_to_your_file.txt";  
  
        try (AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(Paths.get(filePath), StandardOpenOption.READ)) {  
  
            ByteBuffer buffer = ByteBuffer.allocate(1024);  
  
            // 异步读取操作  
            fileChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {  
  
                @Override  
                public void completed(Integer result, ByteBuffer attachment) {  
                    // 当读取完成时调用此方法  
                    if (result == -1) {  
                        // 文件末尾  
                        System.out.println("End of file reached");  
                        return;  
                    }  
  
                    attachment.flip();  
                    while (attachment.hasRemaining()) {  
                        System.out.print((char) attachment.get());  
                    }  
  
                    attachment.clear();  
  
                    // 继续异步读取  
                    fileChannel.read(buffer, 0, attachment, this);  
                }  
  
                @Override  
                public void failed(Throwable exc, ByteBuffer attachment) {  
                    // 当读取失败时调用此方法  
                    exc.printStackTrace();  
                }  
            });  
  
            // 防止主线程退出  
            Thread.sleep(Long.MAX_VALUE);  
  
        } catch (IOException | InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}
相关推荐
自律的kkk5 天前
网络编程中的黏包和半包问题
java·开发语言·网络·网络编程·tcp·nio
power-辰南6 天前
Netty 常见面试题原理解析
java·开发语言·netty·nio
生活百般滋味,人生需要笑对。 --佚名9 天前
NIO 三大组件
java·开发语言·nio
鹏码纵横14 天前
如何解决 java.nio.charset.CoderMalfunctionError: 编码器故障错误问题?亲测有效的解决方法!
java·python·nio
智商低情商凑14 天前
NIO - selector简单介绍
java·开发语言·nio
智商低情商凑17 天前
Netty - NIO基础学习
nio
小猫猫猫◍˃ᵕ˂◍18 天前
NIO和零拷贝
linux·服务器·nio
程序猿进阶18 天前
ChannelInboundHandlerAdapter 与 SimpleChannelInboundHandler 的区别
java·开发语言·后端·面试·性能优化·netty·nio
橘子真甜~19 天前
Linux操作系统3-文件与IO操作1(从C语言IO操作到系统调用)
linux·运维·服务器·c语言·io·文件操作·文件fd
材料苦逼不会梦到计算机白富美19 天前
多人聊天室 NIO模型实现
运维·服务器·nio