转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7794151.html html
前面讲到:Java IO编程全解(四)——NIO编程java
NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供两种方式获取操做结果。编程
CompletionHandler接口的实现类做为操做完成的回调。服务器
NIO2.0的异步套接字通道是真正的异步非阻塞I/O,它对UNIX网络编程中的事件驱动I/O(AIO),它不须要经过多路复用器(Selector)对注册的通道进行轮询操做便可实现异步读写,从而简化了NIO的编程模型。微信
下面经过代码来熟悉NIO2.0 AIO的相关类库,仍旧以时间服务器为例程进行讲解。网络
package joanna.yan.aio; public class TimeServer { public static void main(String[] args) { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 采用默认值 } } AsyncTimeServerHandler timeServer=new AsyncTimeServerHandler(port); new Thread(timeServer,"AIO-AsyncTimeServerHandler-001").start(); } }
package joanna.yan.aio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.AsynchronousServerSocketChannel; import java.util.concurrent.CountDownLatch; public class AsyncTimeServerHandler implements Runnable{ private int port; CountDownLatch latch; AsynchronousServerSocketChannel asynchronousServerSocketChannel; public AsyncTimeServerHandler(int port){ this.port=port; try { //建立一个异步的服务端通道AsynchronousServerSocketChannel asynchronousServerSocketChannel=AsynchronousServerSocketChannel.open(); asynchronousServerSocketChannel.bind(new InetSocketAddress(port)); System.out.println("The time server is start in port: "+port); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { /* *初始化CountDownLatch对象。 *它的做用是,在完成一组正在执行的操做以前,容许当前的线程一直阻塞。 *在本例中,咱们让线程在此阻塞,防止服务器执行完成退出。 *在实际项目应用中,不须要启动独立的线程来处理AsynchronousServerSocketChannel,这里仅仅是个demo演示。 */ latch=new CountDownLatch(1); doAccept(); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 接收客户端的链接。 * 因为这里是异步操做。咱们能够传递一个CompletionHandler<AsynchronousSocketChannel,? super A>类型 * 的handler实例接收accept操做成功的通知消息 */ public void doAccept() { asynchronousServerSocketChannel.accept(this, new AcceptCompletionHandler()); } }
package joanna.yan.aio; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; /** * 接收accept操做成功的通知消息 * @author Administrator * */ public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel,AsyncTimeServerHandler>{ @Override public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) { /* * 疑惑:既然已经接收客户端成功了,为何还要再次调用accept方法呢? * 缘由:当咱们调用AsynchronousServerSocketChannel的accept方法后,若是有新的客户端链接接入, * 系统将回调咱们传入的CompletionHandler实例的completed方法,表示新的客户端已经接入成功, * 由于一个AsynchronousServerSocketChannel能够接收成千上万个客户端,因此咱们须要继续调用它的accep方法, * 接收其余的客户端链接,最终造成一个循环。 * 每当接收一个客户读链接成功以后,再异步接收新的客户端链接。 */ attachment.asynchronousServerSocketChannel.accept(attachment, this); ByteBuffer buffer=ByteBuffer.allocate(1024); /* * 参数一:接收缓冲区,用于从异步Channel中读取数据包; * 参数二:异步Channel携带的附件,通知回调的时候做为入参使用; * 参数三:接收通知回调的业务handler */ result.read(buffer, buffer, new ReadCompletionHandler(result)); } @Override public void failed(Throwable exc, AsyncTimeServerHandler attachment) { exc.printStackTrace(); attachment.latch.countDown(); } }
package joanna.yan.aio; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.Date; /** * 主要用于读取半包消息和发送应答。 * 本例不对半包读写进行具体说明,在后面的Netty半包处理中会介绍。 * @author Administrator * */ public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer>{ private AsynchronousSocketChannel channel; public ReadCompletionHandler(AsynchronousSocketChannel channel){ if(this.channel==null){ this.channel=channel; } } @Override public void completed(Integer result, ByteBuffer attachment) { //为后续从缓冲区读取数据作准备 attachment.flip(); byte[] body=new byte[attachment.remaining()]; attachment.get(body); try { String req=new String(body, "UTF-8"); System.out.println("The time server receive order : "+req); String currentTime="QUERY TIME ORDER".equalsIgnoreCase(req) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; doWrite(currentTime); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** * 发生异常的时候调用。 * 对异常Throwable进行判断,若是是I/O异常,就关闭链路,释放资源; * 若是是其它异常,按照业务本身的逻辑进行处理。 * 本例做为简单demo,没有对异常进行分类判断,只要发生了读写异常,就关闭链路,释放资源。 */ @Override public void failed(Throwable exc, ByteBuffer attachment) { try { this.channel.close(); } catch (IOException e) { e.printStackTrace(); } } private void doWrite(String currentTime) { if(currentTime!=null&¤tTime.trim().length()>0){ byte[] bytes=currentTime.getBytes(); ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length); writeBuffer.put(bytes); writeBuffer.flip(); channel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer buffer) { //若是没有发送完成,继续发送 if(buffer.hasRemaining()){ channel.write(buffer, buffer, this); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { try { channel.close(); } catch (IOException e) { //ingnore on close } } }); } } }
package joanna.yan.aio; public class TimeClient { public static void main(String[] args) { int port=9090; if(args!=null&&args.length>0){ try { port=Integer.valueOf(args[0]); } catch (Exception e) { // 采用默认值 } } /* * 经过一个独立的I/O线程建立异步时间服务器客户端handler。 * 在实际项目中,咱们不须要独立的线程建立异步链接对象,由于底层都是经过JDK的系统回调实现的。 */ new Thread(new AsyncTimeClientHandler("127.0.0.1", port),"AIO-AsyncTimeClientHandle-001").start(); /* * 须要指出的是,正如以前的NIO例程,咱们并无完整的处理网络的半包读写,在对例程进行功能测试的是尚未问题, * 可是,若是对代码稍加改造,进行压力或者性能测试,就会发现输出结果存在问题。 * 这里只集中将NIO的入门知识,后面会详细讲到半包读写 */ } }
package joanna.yan.aio; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.CountDownLatch; public class AsyncTimeClientHandler implements CompletionHandler<Void, AsyncTimeClientHandler>,Runnable{ private AsynchronousSocketChannel client; private String host; private int port; private CountDownLatch latch; public AsyncTimeClientHandler(String host,int port){ this.host=host; this.port=port; try { client=AsynchronousSocketChannel.open(); } catch (IOException e) { e.printStackTrace(); } } @Override public void completed(Void result, AsyncTimeClientHandler attachment) { byte[] req="QUERY TIME ORDER".getBytes(); ByteBuffer writeBuffer=ByteBuffer.allocate(req.length); writeBuffer.put(req); writeBuffer.flip(); client.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {//用于写操做完成后的回调 @Override public void completed(Integer result, ByteBuffer buffer) { if(buffer.hasRemaining()){ client.write(buffer, buffer, this); }else{ ByteBuffer readBuffer=ByteBuffer.allocate(1024); client.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>(){//当读取完成被JDK回调时,构造应答消息。 @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("Now is : "+body); latch.countDown(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { try { client.close(); //让AsyncTimeClientHandler线程执行完毕,客户端退出执行 latch.countDown(); } catch (IOException e) { //ingnore on close } } }); } } @Override public void failed(Throwable exc, ByteBuffer attachment) { try { client.close(); latch.countDown(); } catch (IOException e) { //ingnore on close } } }); } @Override public void failed(Throwable exc, AsyncTimeClientHandler attachment) { exc.printStackTrace(); try { client.close(); latch.countDown(); } catch (IOException e) { //ingnore on close } } @Override public void run() { //建立CountDownLatch进行等待,防止异步操做没有执行完成线程就退出。 latch=new CountDownLatch(1); /* * 参数二:AsynchronousSocketChannel的附件,用于回调通知时做为入参被传递,调用者能够自定义 * 参数三:异步参数回调通知接口,由调用者实现。 */ client.connect(new InetSocketAddress(host, port), this, this); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }
异步SocketChannel是被动执行对象,咱们不须要像NIO编程那样建立一个独立I/O线程来处理读写操做。对于AsynchronousServerSocketChannel和 AsynchronousSocketChannel,它们都由JDK底层的线程池负责回调并驱动读写操做。正由于如此,基于NIO2.0新的异步非阻塞Channel进行编程比NIO编程更为简单。异步
后面,咱们将对前面讲到的4种I/O进行概念澄清和比较,让你们从总体上掌握这些I/O模型的差别。以便在将来的工做中可以根据产品的实际状况选择合适的I/O模型。async
Java IO编程全解(六)——4种I/O的对比与选型ide
若是此文对您有帮助,微信打赏我一下吧~源码分析