- AIO 的核心概念:发起非阻塞方式的 I/O 操作。当 I/O 操作完成时通知。
- 应用程序的责任就是:什么时候发起操作? I/O 操作完成时通知谁?
AIO 的 I/O 操作,有两种方式的 API 可以进行:
- Future 方式
- Callback 方式
Future 方式
Future 方式:即提交一个 I/O 操作请求,返回一个 Future。
然后您可以对 Future 进行检查,确定它是否完成,或者阻塞 IO 操作直到操作正常完成或者超时异常。使用 Future 方式很简单,比较典型的代码通常像清单 1 所示。
清单 1. 使用 Future 方式的代码示例
AsynchronousSocketChannel ch = AsynchronousSocketChannel.open();
// 连接远程服务器,等待连接完成或者失败
Future<Void> result = ch.connect(remote);
// 进行其他工作,例如,连接后的准备环境,f.e.
//prepareForConnection();
//Future 返回 null 表示连接成功
if(result.get()!=null){
// 连接失败,清理刚才准备好的环境,f.e.
//clearPreparation();
return;
}
// 网络连接正常建立
...
ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
// 进行读操作
Future<Integer> result = ch.read(buffer);
// 此时可以进行其他工作,f.e.
//prepareLocalFile();
// 然后等待读操作完成
try {
int bytesRead = result.get();
if(bytesRead==-1){
// 返回 -1 表示没有数据了而且通道已经结束,即远程服务器正常关闭连接。
//clear();
return;
}
// 处理读到的内容,例如,写入本地文件,f.e.
//writeToLocolFile(buffer);
} catch (ExecutionExecption x) {
//failed
}
需要注意的是,因为 Future.get()
是同步的,所以如果不仔细考虑使用场合,使用 Future 方式可能很容易进入完全同步的编程模式,从而使得异步操作成为一个摆设。如果这样,那么原来旧版本的 Socket API 便可以完全胜任,大可不必使用异步 I/O。
Callback 方式
Callback 方式:即提交一个 I/O 操作请求,并且指定一个 CompletionHandler
。
当异步 I/O 操作完成时,便发送一个通知,此时这个 CompletionHandler 对象的 completed 或者 failed 方法将会被调用,样例代码如清单 2 所示。
V - The result type of the I/O operation I/O操作返回结果的类型
A - The type of the object attached to the I/O operation 附加到I/O操作上的对象类型
public interface CompletionHandler<V,A> {
// The completed method is invoked when the I/O operation completes successfully.
// result 表示I/O操作结果,
// attachment 表示初始化是附加I/O操作上的对象
void completed(V result, A attachment);
// The failed method is invoked if the I/O operations fails
// exc 参数表示失败原因 ,attachment 参数同上。
void failed(Throwable exc, A attachment);
}
AIO 提供四种类型的异步通道以及不同的 I/O 操作接受一个 CompletionHandler 对象,它们分别是:
- AsynchronousSocketChannel:connect,read,write
- AsynchronousFileChannel:lock,read,write
- AsynchronousServerSocketChannel:accept
- AsynchronousDatagramChannel:read,write,send,receive
AsynchronousSocketChannel
主要方法
#此方法初始化一个异步操作用以接受对此通道建立的socket连接,返回表示待处理结果的Future对象
public abstract Future<AsynchronousSocketChannel> accept()
#此方法初始化一个异步操作用以接受对此通道建立的socket连接
#当接收了一个socket链接(或失败)后,会调用CompletionHandler中相应的方法
public abstract <A> void accept(A attachment,CompletionHandler<AsynchronousSocketChannel,? super A> handler)
public final AsynchronousServerSocketChannel bind(SocketAddress local) throws IOException
public abstract AsynchronousServerSocketChannel bind(SocketAddress local,int backlog) throws IOException
#打开一个异步server-socket通道
public static AsynchronousServerSocketChannel open() throws IOException
public static AsynchronousServerSocketChannel open(AsynchronousChannelGroup group) throws IOException
AsynchronousSocketChannel
主要方法
abstract AsynchronousSocketChannel bind(SocketAddress local)
abstract Future<Void> connect(SocketAddress remote)
abstract <A> void connect(SocketAddress remote, A attachment, CompletionHandler<Void,? super A> handler)
abstract SocketAddress getLocalAddress()
abstract SocketAddress getRemoteAddress()
static AsynchronousSocketChannel open()
static AsynchronousSocketChannel open(AsynchronousChannelGroup group)
AsynchronousChannelProvider provider()
abstract Future<Integer> read(ByteBuffer dst)
abstract <A> void read(ByteBuffer[] dsts, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler<Long,? super A> handler)
<A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer,? super A> handler)
abstract <A> void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer,? super A> handler)
#Shutdown the connection for reading without closing the channel.
abstract AsynchronousSocketChannel shutdownInput()
#Shutdown the connection for writing without closing the channel.
abstract AsynchronousSocketChannel shutdownOutput()
#Writes a sequence of bytes to this channel from the given buffer.
abstract Future<Integer> write(ByteBuffer src)
abstract <A> void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler<Long,? super A> handler)
<A> void write(ByteBuffer src, A attachment, CompletionHandler<Integer,? super A> handler)
abstract <A> void write(ByteBuffer src, long timeout, TimeUnit unit, A attachment, CompletionHandler<Integer,? super A> handler)
程序示例
package com.aio2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class AioServer implements Runnable {
private static ExecutorService executorService = Executors.newFixedThreadPool(4);
AsynchronousServerSocketChannel serverSocketChannel = null;
private int port;
public AioServer(int port) throws IOException {
super();
serverSocketChannel = AsynchronousServerSocketChannel.open();
this.port = port;
}
public AsynchronousServerSocketChannel getServerChannel() {
return serverSocketChannel;
}
@Override
public void run() {
try {
AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(executorService);
serverSocketChannel.bind(new InetSocketAddress(port));
serverSocketChannel.accept(this, new ServerSocketHandler());
group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (Exception e) {
System.out.println("异常:" + e);
}
}
}
package com.aio2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class ServerSocketHandler implements CompletionHandler<AsynchronousSocketChannel, AioServer> {
@Override
public void completed(AsynchronousSocketChannel result, AioServer attachment) {
// 处理下一次的client连接。类似链式调用
attachment.getServerChannel().accept(attachment, this);
try {
System.out.println("服务器收到客户端" + result.getRemoteAddress().toString());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 执行业务逻辑
doRead(result);
}
@Override
public void failed(Throwable exc, AioServer attachment) {
System.out.println("channel accept failed");
}
private void doRead(AsynchronousSocketChannel asynchronousSocketChannel) {
// 创建数据的缓存区对象
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 从通道中读取客户端传送的数据到缓冲区中
asynchronousSocketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer byteBuffer) {
// 从缓冲区中get数据并进行解析计算
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
String formulaStr = new String(bytes);
try {
System.out.println(
"客户端(" + asynchronousSocketChannel.getRemoteAddress().toString() + ")发送公式:" + formulaStr);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 进行计算,并将结果放入到缓冲区中
String stra = formulaStr.substring(0, formulaStr.indexOf("+"));
String strb = formulaStr.substring(formulaStr.indexOf("+") + 1, formulaStr.length());
int a = Integer.parseInt(stra);
int b = Integer.parseInt(strb);
int sum = a + b;
String returnResult = formulaStr + "=" + sum;
try {
System.out.println(
"客户端(" + asynchronousSocketChannel.getRemoteAddress().toString() + ")计算结果:" + returnResult);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 向客户端写数据
doWrite(asynchronousSocketChannel, returnResult);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("channel read failed");
}
});
}
private void doWrite(AsynchronousSocketChannel asynchronousSocketChannel, String str) {
// 创建数据的缓存区对象
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
// 从缓冲区中读取数据到通道发向客户端
byteBuffer.flip();
asynchronousSocketChannel.write(byteBuffer);
// 也可以使用asynchronousSocketChannel.write(byteBuffer).get();
// try {
// asynchronousSocketChannel.write(byteBuffer).get();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (ExecutionException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
}
package com.aio2;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Random;
import java.util.concurrent.ExecutionException;
public class AioClient {
AsynchronousSocketChannel socketChannel = null;
InetSocketAddress addr = null;
public AioClient(InetSocketAddress addr) throws IOException {
this.addr = addr;
socketChannel = AsynchronousSocketChannel.open();
socketChannel.connect(addr);
}
public void writeFormula() {
Random df = new Random();
// 产生两个加法随机数
int num1 = df.nextInt(101);
int num2 = df.nextInt(101);
// 拼接两个随机数产生加法公式
String str = num1 + "+" + num2;
System.out.println("生成公式:" + str);
// 创建数据缓存区对象
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
// 将缓冲区数据写入到通道中
byteBuffer.flip();
socketChannel.write(byteBuffer);
}
public void readFormulaResult() {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 读取通道中从服务器端来的数据到缓冲区中
/**
* read() 是一个异步方法,实际由OS实现, get()会阻塞,此处使用阻塞是因为后面要把结果打印 也可以去掉get,但是就必须实现
* CompletionHandler 就像server端读取数据那样
*/
try {
socketChannel.read(byteBuffer).get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byteBuffer.flip();
// 通过缓冲区有效元素大小确定接收数组大小
byte[] bytes = new byte[byteBuffer.remaining()];
// 从缓冲区读取数据
byteBuffer.get(bytes);
String stringBuffer = new String(bytes);
System.out.println("服务器端计算结果:" + stringBuffer);
}
}
package com.aio2;
import java.io.IOException;
/**
* 启动服务器端线程
*
* @author yaosht
*
*/
public class StartServer {
public static void main(String[] args) throws IOException {
new Thread(new AioServer(8080), "AioServerThread").start();
}
}
package com.aio2;
import java.io.IOException;
import java.net.InetSocketAddress;
/**
* 客户端启动
*
* @author yaosht
*
*/
public class StartClient {
public static void main(String[] args) throws IOException {
for (int i = 0; i < 4; i++) {
AioClient clientThread = new AioClient(new InetSocketAddress("127.0.0.1", 8080));
clientThread.writeFormula();
clientThread.readFormulaResult();
}
}
}
运行结果