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();  
        }  
    }  
}
相关推荐
Eugene__Chen4 天前
java IO/NIO/AIO
java·python·nio
一个儒雅随和的男子4 天前
Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例
nio
Craaaayon5 天前
JVM虚拟机--JVM的组成
java·jvm·nio
李boyang6 天前
C++ IO流
c++·io
森叶8 天前
Java NIO & Java 虚拟线程(微线程)与 Go 协程的运行原理不同 为何Go 能在低配机器上承接10万 Websocket 协议连接
java·websocket·nio
码熔burning8 天前
【Netty篇】Channel 详解
netty·nio·channel
码熔burning9 天前
【NIO番外篇】之组件 Selector
java·io·nio·selector
码熔burning12 天前
【NIO番外篇】之组件 Channel
java·nio·channel
zhangpeng45554794013 天前
用Java NIO模拟HTTPS
java·https·nio
这个懒人14 天前
linux下io操作详细解析
开发语言·c++·io