JDK在1.4引入NIO(同步非阻塞)包以后,终于在1.7版本加入了异步IO的AIO。
同步异步阻塞和非阻塞等概念,建议参考 《Unix网络编程》 卷1. ,这里只谈AIO的api。编程
AsynchronousSocketChannelapi
AsynchronousServerSocketChannel网络
AsynchronousFileChannel异步
AsynchronousDatagramChannelsocket
先看下AsynchronousServerSocketChannel,它有两个构造方法,选择哪一个构造方法,也就选择了不一样的编程模型,分别是Future机制和Handler回调机制。Future机制适合须要同步等待获取结果的,Handler机制则看上去则更像纯异步。固然,结果都是同样的,看本身须要或者喜欢哪一个模型吧。工具
public abstract Future<AsynchronousSocketChannel> accept(); public abstract <A> void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler);
先看看AsynchronousServerSocketChannel来启动一个server,代码以下。注意accept,不管用future仍是handler,这里的调用都是非阻塞的当即返回。这里选用handler的方式,若是有客户端链接上来,handler的会被回调。测试
public class AioServer { public final int port = 8080; public final int backlog = 2; //跟bio和nio的backlog实际上是同样的。指定accpet等待队列的长度 private AioAcceptHandler acceptHandler; private AsynchronousServerSocketChannel serverSocket; public static void main(String[] args) throws Exception { new AioServer().startup(); } private void startup() throws Exception { int availableProcessors = Runtime.getRuntime().availableProcessors(); ExecutorService channelWorkers = Executors.newFixedThreadPool(availableProcessors * 2); AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withCachedThreadPool(channelWorkers , 1); serverSocket = AsynchronousServerSocketChannel.open(channelGroup); serverSocket.bind(new InetSocketAddress(port), backlog); acceptHandler = new AioAcceptHandler(); accept(); } public void accept() { serverSocket.accept(this, acceptHandler); //非阻塞 } }
接下来CompletionHandler的接口吧,代码以下。看到泛型的 V result 就说嘛,当咱们使用CompletionHandler的时候,本身须要清楚的知道返回结果是什么。
好比accept中指定的CompletionHandler。它是处理接受链接的,成功返回的话,结果就是套接字,那么咱们就要指定泛型V的实际类型为AsynchronousSocketChannel。
再好比read方法中指定CompletionHandler。read是将数据读取到ByteBuffer,而回调CompletionHandler的时候,结果V是读取的数量。因此咱们就要指定泛型V的实际类型为Integer。
具体能够看下面的实现代码 AioAcceptHandler 和 AioReadHandler。this
public interface CompletionHandler<V,A> { //调用结果,附件 void completed(V result, A attachment); //异常对象、附件 void failed(Throwable exc, A attachment); }
AioAcceptHandlercode
public class AioAcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AioServer>{ public void completed(AsynchronousSocketChannel socket, AioServer aioServer) { try { System.out.printf("客户端%s链接成功.\n", socket.getRemoteAddress().toString()); readData(socket); } catch (Exception e) { e.printStackTrace(); try { socket.close(); } catch (IOException e1) {} } finally { aioServer.accept(); } } private void readData(AsynchronousSocketChannel socket) { ByteBuffer buf = ByteBuffer.allocate(32); //测试时,能够不设置太大,观察aio的屡次read socket.read(buf, buf, new AioReadHandler(socket)); } public void failed(Throwable exc, AioServer aioServer) { exc.printStackTrace(); } }
AioReadHandlerserver
public class AioReadHandler implements CompletionHandler<Integer, ByteBuffer> { private AsynchronousSocketChannel socket; private ByteArrayOutputStream baos = new ByteArrayOutputStream(); public AioReadHandler(AsynchronousSocketChannel socket){ this.socket = socket; } public void completed(Integer result, ByteBuffer buf) { System.out.println("result = " + result + " buf = " + buf); if (result > 0) { buf.flip(); try { baos.write(buf.array()); } catch (IOException e) { e.printStackTrace(); } buf.clear(); socket.read(buf, buf, this); } else if (result == -1) { //result为-1的时候,客户端的socket已经正常关闭。 try { System.out.printf("客户端%s已经断开.\n", socket.getRemoteAddress().toString()); String info = new String(baos.toByteArray(), Charset.forName("UTF8")); System.out.println(info); } catch (Exception e) { e.printStackTrace(); } finally { buf = null; try { socket.close(); } catch (IOException e) {} } } } public void failed(Throwable exc, ByteBuffer buf) { exc.printStackTrace(); } }
基本上,一个简单的AIOServer就上面这点代码拉。实际上我感受代码比NIO用Selector的方式仍是简单清晰多了。至于aio的原理,实际上就是去看看epoll等资料就知道了。
这里附上一个测试的客户端代码:
public class BioClient { public static void main(String[] args) throws Exception { String txt = "美国在发布的朝鲜军力评估报告中也认为:“朝鲜发展航天运载工具对开发射程可达美国的远程导弹意义重要,射程也许能覆盖美国部分地区。不过,因为朝鲜尚未研发出可以重返大气层的运载火箭,因此‘大浦洞2号’尚不具有搭载弹头的攻击能力。”"; Socket socket = new Socket("localhost", 8080); PrintStream print = new PrintStream(socket.getOutputStream()); print.print(txt); print.close(); socket.close(); System.out.println("ok."); } }