AIO 核心概念讲解
一、AIO 是什么?
AIO = Asynchronous IO(异步 IO)
与 BIO、NIO 的架构对比:
AIO(异步式):
┌──────────────┐ ┌──────────────┐
│ Server │ │ Client 1 │
│ │◀───Channel 1───────│ │
│ │ └──────────────┘
│ 系统线程池 │ ↓
│ (自动管理) │ 异步回调通知
│ │ ┌──────────────┐
│CompletionHandler◀──Channel 2───────│ Client 2 │
│ (回调处理) │ └──────────────┘
│ │ ↓
│ 操作完成后 │ 异步回调通知
│ 自动通知 │ ┌──────────────┐
│ │◀───Channel 3───────│ Client 3 │
└──────────────┘ └──────────────┘
↓
异步回调通知
核心:操作完成后,系统主动通知你(打电话通知)
AIO 的核心特点:
- 异步:调用方法后立即返回,操作在后台进行
- 回调机制:操作完成后,系统自动调用回调函数
- 无需轮询:不需要像 NIO 那样用 Selector 轮询
- 系统管理线程:线程由系统管理,不需要自己创建
二、AIO 的核心组件
1. AsynchronousServerSocketChannel(异步服务端通道)
作用: 接受客户端连接
与 BIO/NIO 对比:
| BIO | NIO | AIO |
|---|---|---|
| ServerSocket | ServerSocketChannel | AsynchronousServerSocketChannel |
| 阻塞 | 非阻塞(需轮询) | 异步(回调) |
// 创建
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
// 绑定端口
server.bind(new InetSocketAddress(7397));
// 接受连接(异步)
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel client, Object attachment) {
// 连接成功,自动调用这个方法
// 重要:继续接受下一个连接
server.accept(null, this);
}
@Override
public void failed(Throwable exc, Object attachment) {
// 连接失败
}
});
2. AsynchronousSocketChannel(异步客户端通道)
作用: 读写数据
与 BIO/NIO 对比:
| BIO | NIO | AIO |
|---|---|---|
| Socket | SocketChannel | AsynchronousSocketChannel |
| 阻塞 | 非阻塞 | 异步 |
// 创建
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
// 连接(异步)
client.connect(new InetSocketAddress("127.0.0.1", 7397), null,
new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
// 连接成功
}
});
// 读取(异步)
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer bytesRead, ByteBuffer attachment) {
// 读取成功
}
});
// 写入(异步)
ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());
client.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer bytesWritten, ByteBuffer attachment) {
// 写入成功
}
});
3. CompletionHandler(完成处理器)
作用: 回调接口,操作完成后自动调用
这是 AIO 的核心!
// 泛型参数:
// - 第一个:操作结果类型
// - 第二个:附加对象类型
new CompletionHandler<Integer, ByteBuffer>() {
/**
* 操作成功时调用
* @param result 操作结果(如读取的字节数)
* @param attachment 附加对象(如缓冲区)
*/
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 处理成功的情况
}
/**
* 操作失败时调用
* @param exc 异常信息
* @param attachment 附加对象
*/
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
// 处理失败的情况
}
}
为什么需要 attachment(附加对象)?
// 问题:在回调中需要使用缓冲区,但回调是异步的
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, ???, handler); // 怎么把 buffer 传给回调?
// 解决:通过 attachment 传递
channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// attachment 就是传入的 buffer
attachment.flip();
// 可以使用 buffer 了
}
});
4. ByteBuffer(字节缓冲区)
作用: 数据容器(与 NIO 相同)
三、SimpleAioServer 详解
类的作用:
使用 AIO 实现的服务端,异步处理客户端连接和消息。
核心流程图:
SimpleAioServer 启动流程:
1. 创建 AsynchronousServerSocketChannel
↓
2. 绑定端口 7397
↓
3. 调用 accept() 接受连接(异步)
↓
4. 主线程 sleep(等待)
↓
(后台线程处理连接)
↓
5. 客户端连接 → completed() 被调用
↓
6. 在 completed() 中:
├─ 再次调用 accept()(继续接受下一个连接)
├─ 发送欢迎消息
└─ 调用 read() 读取数据(异步)
↓
7. 客户端发消息 → read 的 completed() 被调用
↓
8. 在 read 的 completed() 中:
├─ 处理消息
├─ 调用 write() 回复(异步)
└─ 在 write 的 completed() 中:
└─ 再次调用 read()(继续读取下一条消息)
核心代码解析:
public static void main(String[] args) throws IOException, InterruptedException {
// ========== 步骤1-2:初始化 ==========
// 1. 创建异步服务端通道
AsynchronousServerSocketChannel serverChannel =
AsynchronousServerSocketChannel.open();
// 2. 绑定端口
serverChannel.bind(new InetSocketAddress(7397));
System.out.println("AIO 服务端启动,监听端口 7397...");
// ========== 步骤3:接受连接(异步)==========
// 参数1:附加对象(这里不需要,传 null)
// 参数2:回调处理器
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
/**
* 连接成功时,系统自动调用这个方法
*
* @param clientChannel 客户端通道
* @param attachment 附加对象(这里是 null)
*/
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
try {
System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());
// 重要!继续接受下一个客户端连接
// 因为 accept() 只接受一个连接
// 要继续接受,必须再次调用
serverChannel.accept(null, this);
// 发送欢迎消息
String welcome = "欢迎连接到 AIO 服务器!\n";
ByteBuffer welcomeBuffer = ByteBuffer.wrap(
welcome.getBytes(StandardCharsets.UTF_8)
);
clientChannel.write(welcomeBuffer);
// 读取客户端消息
readMessage(clientChannel);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 连接失败时调用
*/
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("接受连接失败:" + exc.getMessage());
}
});
// ========== 步骤4:主线程等待 ==========
// 为什么要 sleep?
// 因为 AIO 是异步的,操作在后台线程执行
// 如果主线程退出,程序就结束了
Thread.sleep(Long.MAX_VALUE);
}
读取消息的实现:
private static void readMessage(AsynchronousSocketChannel clientChannel) {
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 异步读取数据
// 参数1:缓冲区(数据读到这里)
// 参数2:附加对象(传递 buffer,方便在回调中使用)
// 参数3:回调处理器
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
/**
* 读取成功时调用
*
* @param bytesRead 读取的字节数
* @param attachment 附加对象(这里是 buffer)
*/
@Override
public void completed(Integer bytesRead, ByteBuffer attachment) {
try {
if (bytesRead == -1) {
// 客户端断开
System.out.println("客户端断开:" + clientChannel.getRemoteAddress());
clientChannel.close();
return;
}
// 切换为读模式
attachment.flip();
// 读取数据
byte[] bytes = new byte[attachment.remaining()];
attachment.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8).trim();
System.out.println("收到消息:" + message);
// 回复客户端
String response = "服务端已收到:" + message + "\n";
ByteBuffer responseBuffer = ByteBuffer.wrap(
response.getBytes(StandardCharsets.UTF_8)
);
// 异步写入
clientChannel.write(responseBuffer, responseBuffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 写入完成
if ("bye".equalsIgnoreCase(message)) {
try {
System.out.println("客户端主动断开:" +
clientChannel.getRemoteAddress());
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
// 继续读取下一条消息
readMessage(clientChannel);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("写入失败:" + exc.getMessage());
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读取失败时调用
*/
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("读取失败:" + exc.getMessage());
try {
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
关键点:
- accept() 只接受一个连接:必须在 completed() 中再次调用
- 异步操作立即返回:不会阻塞线程
- 回调嵌套:读取 → 写入 → 读取(容易形成回调地狱)
- 主线程要等待:否则程序会立即退出
四、SimpleAioClient 详解
类的作用:
使用 AIO 实现的客户端,异步连接服务端并收发消息。
核心流程图:
SimpleAioClient 启动流程:
1. 创建 AsynchronousSocketChannel
↓
2. 调用 connect() 连接服务端(异步)
↓
3. 主线程 sleep(等待)
↓
(后台线程处理连接)
↓
4. 连接成功 → completed() 被调用
↓
5. 在 completed() 中:
├─ 调用 read() 读取服务端消息(异步)
└─ 启动用户输入线程
↓
6. 服务端发消息 → read 的 completed() 被调用
├─ 打印消息
└─ 再次调用 read()(继续读取)
↓
7. 用户输入 → 调用 write() 发送(异步)
核心代码解析:
public static void main(String[] args) throws IOException, InterruptedException {
// 1. 创建异步客户端通道
clientChannel = AsynchronousSocketChannel.open();
// 2. 连接服务端(异步)
clientChannel.connect(new InetSocketAddress("127.0.0.1", 7397), null,
new CompletionHandler<Void, Object>() {
/**
* 连接成功时调用
*/
@Override
public void completed(Void result, Object attachment) {
System.out.println("已连接到服务端");
// 读取服务端的欢迎消息
readMessage();
// 启动用户输入线程
startInputThread();
}
/**
* 连接失败时调用
*/
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接失败:" + exc.getMessage());
}
});
// 3. 主线程等待
Thread.sleep(Long.MAX_VALUE);
}
读取消息的实现:
private static void readMessage() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 异步读取
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer bytesRead, ByteBuffer attachment) {
if (bytesRead == -1) {
// 服务端断开
System.out.println("服务端断开连接");
System.exit(0);
return;
}
// 切换为读模式
attachment.flip();
// 读取数据
byte[] bytes = new byte[attachment.remaining()];
attachment.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8).trim();
System.out.println("服务端回复:" + message);
// 继续读取下一条消息
readMessage();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("读取失败:" + exc.getMessage());
}
});
}
发送消息的实现:
private static void sendMessage(String message) {
ByteBuffer buffer = ByteBuffer.wrap(
(message + "\n").getBytes(StandardCharsets.UTF_8)
);
// 异步写入
clientChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 写入完成
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("发送失败:" + exc.getMessage());
}
});
}
五、BIO vs NIO vs AIO 完整对比
代码对比:
BIO(阻塞式):
// 服务端
ServerSocket server = new ServerSocket(7397);
while (true) {
Socket socket = server.accept(); // 阻塞
new Thread(() -> {
String msg = reader.readLine(); // 阻塞
}).start();
}
NIO(非阻塞式):
// 服务端
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 阻塞,但监控多个通道
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) { /* 接受连接 */ }
if (key.isReadable()) { /* 读取数据 */ }
}
}
AIO(异步式):
// 服务端
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
server.bind(new InetSocketAddress(7397));
server.accept(null, new CompletionHandler<>() {
public void completed(AsynchronousSocketChannel client, Object attachment) {
server.accept(null, this); // 继续接受
client.read(buffer, buffer, readHandler); // 异步读取
}
});
Thread.sleep(Long.MAX_VALUE); // 等待
六、AIO 代码示例
/**
* 最简单的 AIO 服务端
*
* AIO 核心:异步 + 回调
* 操作完成后,系统自动调用回调函数
*/
public class SimpleAioServer {
public static void main(String[] args) throws IOException, InterruptedException {
// 1. 创建异步服务端通道
AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
// 2. 绑定端口
serverChannel.bind(new InetSocketAddress(7397));
System.out.println("AIO 服务端启动,监听端口 7397...");
// 3. 接受连接(异步)
serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
try {
System.out.println("新客户端连接:" + clientChannel.getRemoteAddress());
// 重要!继续接受下一个客户端连接
serverChannel.accept(null, this);
// 发送欢迎消息
String welcome = "欢迎连接到 AIO 服务器!\n";
ByteBuffer welcomeBuffer = ByteBuffer.wrap(welcome.getBytes(StandardCharsets.UTF_8));
clientChannel.write(welcomeBuffer);
// 读取客户端消息
readMessage(clientChannel);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("接受连接失败:" + exc.getMessage());
}
});
// 4. 主线程等待(因为 AIO 是异步的,操作在后台线程执行)
Thread.sleep(Long.MAX_VALUE);
}
/**
* 读取客户端消息
*/
private static void readMessage(AsynchronousSocketChannel clientChannel) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 异步读取数据
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer bytesRead, ByteBuffer attachment) {
try {
if (bytesRead == -1) {
System.out.println("客户端断开:" + clientChannel.getRemoteAddress());
clientChannel.close();
return;
}
// 切换为读模式
attachment.flip();
// 读取数据
byte[] bytes = new byte[attachment.remaining()];
attachment.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8).trim();
System.out.println("收到消息:" + message);
// 回复客户端
String response = "服务端已收到:" + message + "\n";
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));
// 异步写入
clientChannel.write(responseBuffer, responseBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
if ("bye".equalsIgnoreCase(message)) {
try {
System.out.println("客户端主动断开:" + clientChannel.getRemoteAddress());
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
// 继续读取下一条消息
readMessage(clientChannel);
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("写入失败:" + exc.getMessage());
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("读取失败:" + exc.getMessage());
try {
clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
/**
* 最简单的 AIO 客户端
*
* 功能:连接服务端,发送消息,接收回复
*/
public class SimpleAioClient {
private static AsynchronousSocketChannel clientChannel;
public static void main(String[] args) throws IOException, InterruptedException {
// 1. 创建异步客户端通道
clientChannel = AsynchronousSocketChannel.open();
// 2. 连接服务端(异步)
clientChannel.connect(new InetSocketAddress("127.0.0.1", 7397), null,
new CompletionHandler<Void, Object>() {
@Override
public void completed(Void result, Object attachment) {
System.out.println("已连接到服务端");
// 读取服务端的欢迎消息
readMessage();
// 启动用户输入线程
startInputThread();
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接失败:" + exc.getMessage());
}
});
// 3. 主线程等待
Thread.sleep(Long.MAX_VALUE);
}
/**
* 读取服务端消息
*/
private static void readMessage() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer bytesRead, ByteBuffer attachment) {
if (bytesRead == -1) {
System.out.println("服务端断开连接");
System.exit(0);
return;
}
// 切换为读模式
attachment.flip();
// 读取数据
byte[] bytes = new byte[attachment.remaining()];
attachment.get(bytes);
String message = new String(bytes, StandardCharsets.UTF_8).trim();
System.out.println("服务端回复:" + message);
// 继续读取下一条消息
readMessage();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("读取失败:" + exc.getMessage());
}
});
}
/**
* 启动用户输入线程
*/
private static void startInputThread() {
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入消息(输入 bye 退出):");
while (scanner.hasNextLine()) {
String input = scanner.nextLine();
sendMessage(input);
if ("bye".equalsIgnoreCase(input)) {
try {
Thread.sleep(1000);
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 发送消息
*/
private static void sendMessage(String message) {
ByteBuffer buffer = ByteBuffer.wrap((message + "\n").getBytes(StandardCharsets.UTF_8));
clientChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
// 写入完成
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("发送失败:" + exc.getMessage());
}
});
}
}
1311



被折叠的 条评论
为什么被折叠?



