NIO 2:AIO(异步、非阻塞)
- AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的;
- 但是对 AIO 来说,则更加进了一步,它不是在 IO 准备好时再通知线程,而是在 IO 操作已经完成后,再给线程发出通知。因此 AIO 是不会阻塞的,此时我们的业务逻辑将变成一个回调函数,等待 IO 操作完成后,由系统自动触发;
- 与 NIO 不同,当进行读写操作时,只须直接调用 API 的 read 或 write 方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入 read 方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将 write 方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write 方法都是异步的,完成后会主动调用回调函数。 在 JDK1.7 中,这部分内容被称作 NIO.2,主要在 Java.nio.channels 包下增加了下面四个异步通道:
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
- AsynchronousDatagramChannel
- 在 AIO socket 编程中,服务端通道是 AsynchronousServerSocketChannel,这个类提供了一个 open() 静态工厂,一个 bind() 方法用于绑定服务端 IP 地址(还有端口号),另外还提供了 accept() 用于接收用户连接请求。在客户端使用的通道是 AsynchronousSocketChannel,这个通道处理提供 open 静态工厂方法外,还提供了 read 和 write 方法;
- 在 AIO 编程中,发出一个事件(accept read write 等)之后要指定事件处理类(回调函数),AIO 中的事件处理类是 CompletionHandler<V,A>,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调:
- void completed(V result, A attachment);
- void failed(Throwable exc, A attachment);
1. 异步非阻塞连接
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class Server {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel serverSocketChannel =
AsynchronousServerSocketChannel.open()
.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null,
new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
System.out.println("服务器端接收到连接...");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("IO操作失败!");
}
});
System.out.println("服务器端继续....");
while (true) {
}
}
}
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class Client {
public static void main(String[] args) throws Exception {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8888), null,
new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("客户端连接成功");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("客户端失败!");
}
});
System.out.println("客户端继续");
Thread.sleep(30000);
}
}
- 服务器端打印:
- 客户端打印:
2. 同步非阻塞读写
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Server {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open()
.bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null,
new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
System.out.println("服务器端接收到连接...");
ByteBuffer byteBuffer = ByteBuffer.allocate(20);
Future<Integer> readFuture = result.read(byteBuffer);
try {
System.out.println("读取信息:" +
new String(byteBuffer.array(), 0, readFuture.get()));
result.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("IO操作失败!");
}
});
System.out.println("服务器端继续....");
while (true) {
}
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Client {
public static void main(String[] args) throws Exception {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8888), null,
new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("客户端连接成功");
Future<Integer> writeFuture = socketChannel.write(
ByteBuffer.wrap("我来自客户端...".getBytes()));
try {
System.out.println("写入大小:" + writeFuture.get());
socketChannel.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("客户端失败!");
}
});
System.out.println("客户端继续");
Thread.sleep(30000);
}
}
- 服务器端执行结果:
- 客户端执行结果:
3. 异步非阻塞读写
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws IOException {
AsynchronousServerSocketChannel serverSocketChannel =
AsynchronousServerSocketChannel.open().
bind(new InetSocketAddress(8888));
serverSocketChannel.accept(null,
new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel ch, Void attachment) {
serverSocketChannel.accept(null, this);
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.MAX_VALUE / 300);
System.out.println("【服务器】read开始...");
ch.read(byteBuffer, 10, TimeUnit.SECONDS, null,
new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
if (result == -1) {
System.out.println("客户端没有传输数据就close了...");
}
System.out.println("服务器读取数据:" +
new String(byteBuffer.array(), 0, result));
try {
ch.close();
System.out.println("服务器关闭!");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
exc.printStackTrace();
System.out.println(attachment);
System.out.println("【服务器】异常");
}
});
System.out.println("【服务器】read结束...");
}
@Override
public void failed(Throwable exc, Void attachment) {
}
});
System.out.println("服务器开始循环...");
while (true) {
}
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8888), null, new CompletionHandler<Void, Void>() {
@Override
public void completed(Void result, Void attachment) {
socketChannel.write(
ByteBuffer.wrap("你好服务器".getBytes()), null,
new CompletionHandler<Integer, Void>() {
@Override
public void completed(Integer result, Void attachment) {
System.out.println("输出完毕!");
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("输出失败!");
}
});
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Void attachment) {
System.out.println("【客户端】异常!");
}
});
Thread.sleep(1000);
}
}
- 服务器端输出:
- 客户端输出: