AIO需要操作系统的支持,在linux内核2.6版本中加入了对真正异步IO的支持,java从jdk1.7开始支持AIO
核心类有AsynchronousSocketChannel 、AsynchronousServerSocketChannel、AsynchronousChannelGroup
AsynchronousChannelGroup是异步Channel的分组管理器,它可以实现资源共享。
创建AsynchronousChannelGroup时,需要传入一个ExecutorService,也就是绑定一个线程池,该线程池负责两个任务:处理IO事件和触发CompletionHandler回调接口。
accept()
方法:
AsynchronousServerSocketChannel创建成功后,类似于ServerSocket,也是调用accept()
方法来接受来自客户端的连接,
由于异步IO实际的IO操作是交给操作系统来做的,用户进程只负责通知操作系统进行IO和接受操作系统IO完成的通知。
所以异步的ServerChannel调用accept()
方法后,当前线程不会阻塞,
程序也不知道accept()方法什么时候能够接收到客户端请求并且操作系统完成网络IO,
为解决这个问题,AIO为accept()方法提供两个版本:
Future accept()
:AsynchronousSocketChannel
),则应该调用该方法返回的Future对象的get()
方法,但是get()
方法会阻塞该线程,所以这种方式是阻塞式的异步IO。< A > void accept (Aattachment,CompletionHandler handler)
:CompletionHandler
对象的相应方法。AsynchronousSocketChannel
就代表该CompletionHandler
处理器在处理连接成功时的result是AsynchronousSocketChannel
的实例。CompletionHandler
接口中定义了两个方法,completed(V result , A attachment)
:当IO完成时触发该方法,该方法的第一个参数代表IO操作返回的对象,faild(Throwable exc, A attachment)
:当IO失败时触发该方法,第一个参数代表IO操作失败引发的异常或错误。public class AioServer {
private static int DEFAULT_PORT = 12345;
private static ServerHandler serverHandle;
public volatile static long clientCount = 0;
public static void start(){
start(DEFAULT_PORT);
}
public static synchronized void start(int port){
if(serverHandle!=null)
return;
serverHandle = new ServerHandler(port);
new Thread(serverHandle,"Server").start();
}
public static void main(String[] args) {
AioServer.start();
}
}
public class ServerHandler implements Runnable{
private AsynchronousServerSocketChannel channel;
public ServerHandler(int port) {
try {
//创建服务端通道
channel = AsynchronousServerSocketChannel.open();
//绑定端口
channel.bind(new InetSocketAddress(port));
System.out.println("服务端已启动,端口号:"+port);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
channel.accept(this, new AcceptHandler());
// Future accept = channel.accept();
//该步操作是异步操作 防止当前线程直接执行结束
//方案1: while(true)+sleep
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// //方案2 CountDownLatch 作用:在完成一组正在执行的操作之前,允许当前的现场一直阻塞 此处,让现场在此阻塞,防止服务端执行完成后退出
//
// CountDownLatch count = new CountDownLatch(1);
// channel.accept(this, new AcceptHandler());
// try {
// count.await();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
// CompletionHandler
// V-IO操作的结果,这里是成功建立的连接,AsynchronousSocketChannel
// A-IO操作附件,这里传入AsynchronousServerSocketChannel便于继续接收请求建立新连接
class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, ServerHandler> {
@Override
public void completed(AsynchronousSocketChannel channel, ServerHandler serverHandler) {
//创建新的Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//异步读 第三个参数为接收消息回调的业务Handler
// channel.read(buffer, buffer, new ReadHandler(channel));
//继续接受其他客户端请求
serverHandler.channel.accept(null, this);
}
@Override
public void failed(Throwable exc, ServerHandler serverHandler) {
exc.printStackTrace();
}
}
class ReadHandler implements CompletionHandler<ByteBuffer, ByteBuffer> {
//用户读取或者发送消息的channel
private AsynchronousSocketChannel channel;
public ReadHandler(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void completed(ByteBuffer result, ByteBuffer attachment) {
result.flip();
byte[] msg = new byte[result.remaining()];
result.get(msg);
try {
String expression = new String(msg, "UTF-8");
System.out.println("服务器收到消息: " + expression);
// String result1 = "服务端收到消息\n";
result.clear();
//向客户端发送消息
doWrite(expression);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//发送消息
private void doWrite(String msg) {
byte[] bytes = msg.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(bytes);
buffer.flip();
//异步写数据
channel.write(buffer, buffer, new CompletionHandler <Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
//如果没有发送完,继续发送
if (attachment.hasRemaining()) {
channel.write(attachment, attachment, this);
} else {
//创建新的Buffer
ByteBuffer allocate = ByteBuffer.allocate(1024);
//异步读 第三个参数为接收消息回调的业务Handler
// channel.read(allocate, attachment, new ReadHandler(channel));
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class AioClient {
private static String DEFAULT_HOST = "127.0.0.1";
private static int DEFAULT_PORT = 12345;
private static ClientHandler clientHandle;
public static void start(){
start(DEFAULT_HOST,DEFAULT_PORT);
}
public static synchronized void start(String ip,int port){
if(clientHandle!=null)
return;
clientHandle = new ClientHandler(ip,port);
new Thread(clientHandle,"Client").start();
}
//向服务器发送消息
public static boolean sendMsg(String msg) throws Exception{
if(msg.equals("exit")) return false;
clientHandle.sendMsg(msg);
return true;
}
public static void main(String[] args) throws Exception{
AioClient.start();
System.out.println("请输入请求消息:");
Scanner scanner = new Scanner(System.in);
while(AioClient.sendMsg(scanner.nextLine()));
}
}
public class ClientHandler implements Runnable{
private AsynchronousSocketChannel clientChannel;
private String host;
private int port;
private CountDownLatch latch;
public ClientHandler(String host, int port) {
this.host = host;
this.port = port;
try {
//创建异步的客户端通道
clientChannel = AsynchronousSocketChannel.open();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//创建CountDownLatch等待
// latch = new CountDownLatch(1);
//发起异步连接操作,回调参数就是这个类本身,如果连接成功会回调completed方法
clientChannel.connect(new InetSocketAddress(host, port), this, new AcceptHandler());
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// try {
// latch.await();
// } catch (InterruptedException e1) {
// e1.printStackTrace();
// }
// try {
// clientChannel.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
}
//向服务器发送消息
public void sendMsg(String msg){
byte[] req = msg.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
System.out.println(">>>>>>msg:"+msg);
writeBuffer.put(req);
writeBuffer.flip();
//异步写
clientChannel.write(writeBuffer, writeBuffer,new WriteHandler(clientChannel));
}
/**
* 接收类
*/
class AcceptHandler implements CompletionHandler<Void, ClientHandler> {
public AcceptHandler() {
}
@Override
public void completed(Void result, ClientHandler attachment) {
System.out.println("连接服务器成功");
}
@Override
public void failed(Throwable exc, ClientHandler attachment) {
exc.printStackTrace();
try {
attachment.clientChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel channel;
public WriteHandler(AsynchronousSocketChannel channel) {
this.channel = channel;
}
@Override
public void completed(Integer result, ByteBuffer attachment) {
//完成全部数据的写入
if (attachment.hasRemaining()) {
//数据没有写完,继续写
System.out.println("WriteHandler.hasRemaining>>>>>");
clientChannel.write(attachment, attachment, this);
} else {
//读取数据
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
clientChannel.read(readBuffer, readBuffer, new ReadHandler(clientChannel));
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel clientChannel;
public ReadHandler(AsynchronousSocketChannel clientChannel) {
this.clientChannel = clientChannel;
}
@Override
public void completed(Integer result,ByteBuffer buffer) {
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String body;
try {
body = new String(bytes,"UTF-8");
System.out.println("客户端收到结果:"+ body);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc,ByteBuffer attachment) {
System.err.println("数据读取失败...");
try {
clientChannel.close();
} catch (IOException e) {
}
}
}
}