Java NIO之AIO

  • 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();
		}
	}
}

运行结果

    

参考在 Java 7 中体会 NIO.2 异步执行的快乐

Java AIO

基于Java NIO2实现的异步非阻塞消息通信框架

发布了243 篇原创文章 · 获赞 138 · 访问量 138万+

猜你喜欢

转载自blog.csdn.net/ystyaoshengting/article/details/104172555