BIO、NIO和AIO是Java网络编程中的三种主要IO模型。
- BIO(Blocking IO):同步并阻塞,服务实现模式为一个连接对应一个线程,即客户端发送一个连接,服务端要有一个线程来处理。如果连接多了,线程数量不够,就只能等待,即会发生阻塞。BIO适用于连接数目比较小且固定的架构,对服务器要求比较高,并发局限于应用中。
- NIO(Non-Blocking IO):同步非阻塞,服务实现模式是一个线程可以处理多个连接,即客户端发送的连接都会注册到多路复用器上,然后进行轮询连接,有I/O请求就处理。NIO适用于连接数目多且连接比较短的架构,如聊天服务器、弹幕系统等,编程比较复杂。NIO以I/O块(buffer)的形式处理数据,效率比BIO的I/O流更高。NIO是面向缓冲区或面向块编程的,支持双向操作,而BIO则是单向的,要么是输入流要么是输出流。
- 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();
}
}
}