AIO编程(NIO2.0)及代码实现
来源:互联网 发布:淘宝c店详情页banner 编辑:程序博客网 时间:2024/06/16 07:27
AIO编程
NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。
异步的套接字通道时真正的异步非阻塞I/O,对应于UNIX网络编程中的事件驱动I/O(AIO)。他不需要过多的Selector对注册的通道进行轮询即可实现异步读写,从而简化了NIO的编程模型。
直接上代码吧。
3.1、Server端代码
Server:
- package com.anxpp.io.calculator.aio.server;
- /**
- * AIO服务端
- * @author yangtao__anxpp.com
- * @version 1.0
- */
- public class Server {
- private static int DEFAULT_PORT = 12345;
- private static AsyncServerHandler 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 AsyncServerHandler(port);
- new Thread(serverHandle,"Server").start();
- }
- public static void main(String[] args){
- Server.start();
- }
- }
AsyncServerHandler:
- package com.anxpp.io.calculator.aio.server;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.nio.channels.AsynchronousServerSocketChannel;
- import java.util.concurrent.CountDownLatch;
- public class AsyncServerHandler implements Runnable {
- public CountDownLatch latch;
- public AsynchronousServerSocketChannel channel;
- public AsyncServerHandler(int port) {
- try {
- //创建服务端通道
- channel = AsynchronousServerSocketChannel.open();
- //绑定端口
- channel.bind(new InetSocketAddress(port));
- System.out.println("服务器已启动,端口号:" + port);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void run() {
- //CountDownLatch初始化
- //它的作用:在完成一组正在执行的操作之前,允许当前的现场一直阻塞
- //此处,让现场在此阻塞,防止服务端执行完成后退出
- //也可以使用while(true)+sleep
- //生成环境就不需要担心这个问题,以为服务端是不会退出的
- latch = new CountDownLatch(1);
- //用于接收客户端的连接
- channel.accept(this,new AcceptHandler());
- try {
- latch.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
AcceptHandler:
- package com.anxpp.io.calculator.aio.server;
- import java.nio.ByteBuffer;
- import java.nio.channels.AsynchronousSocketChannel;
- import java.nio.channels.CompletionHandler;
- //作为handler接收客户端连接
- public class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncServerHandler> {
- @Override
- public void completed(AsynchronousSocketChannel channel,AsyncServerHandler serverHandler) {
- //继续接受其他客户端的请求
- Server.clientCount++;
- System.out.println("连接的客户端数:" + Server.clientCount);
- serverHandler.channel.accept(serverHandler, this);
- //创建新的Buffer
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- //异步读 第三个参数为接收消息回调的业务Handler
- channel.read(buffer, buffer, new ReadHandler(channel));
- }
- @Override
- public void failed(Throwable exc, AsyncServerHandler serverHandler) {
- exc.printStackTrace();
- serverHandler.latch.countDown();
- }
- }
ReadHandler:
- package com.anxpp.io.calculator.aio.server;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.nio.ByteBuffer;
- import java.nio.channels.AsynchronousSocketChannel;
- import java.nio.channels.CompletionHandler;
- import com.anxpp.io.utils.Calculator;
- public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
- //用于读取半包消息和发送应答
- private AsynchronousSocketChannel channel;
- public ReadHandler(AsynchronousSocketChannel channel) {
- this.channel = channel;
- }
- //读取到消息后的处理
- @Override
- public void completed(Integer result, ByteBuffer attachment) {
- //flip操作
- attachment.flip();
- //根据
- byte[] message = new byte[attachment.remaining()];
- attachment.get(message);
- try {
- String expression = new String(message, "UTF-8");
- System.out.println("服务器收到消息: " + expression);
- String calrResult = null;
- try{
- calrResult = Calculator.cal(expression).toString();
- }catch(Exception e){
- calrResult = "计算错误:" + e.getMessage();
- }
- //向客户端发送消息
- doWrite(calrResult);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- //发送消息
- private void doWrite(String result) {
- byte[] bytes = result.getBytes();
- ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
- writeBuffer.put(bytes);
- writeBuffer.flip();
- //异步写数据 参数与前面的read一样
- channel.write(writeBuffer, writeBuffer,new CompletionHandler<Integer, ByteBuffer>() {
- @Override
- public void completed(Integer result, ByteBuffer buffer) {
- //如果没有发送完,就继续发送直到完成
- if (buffer.hasRemaining())
- channel.write(buffer, buffer, this);
- else{
- //创建新的Buffer
- ByteBuffer readBuffer = ByteBuffer.allocate(1024);
- //异步读 第三个参数为接收消息回调的业务Handler
- channel.read(readBuffer, readBuffer, new ReadHandler(channel));
- }
- }
- @Override
- public void failed(Throwable exc, ByteBuffer attachment) {
- try {
- channel.close();
- } catch (IOException e) {
- }
- }
- });
- }
- @Override
- public void failed(Throwable exc, ByteBuffer attachment) {
- try {
- this.channel.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
OK,这样就已经完成了,其实说起来也简单,虽然代码感觉很多,但是API比NIO的使用起来真的简单多了,主要就是监听、读、写等各种CompletionHandler。此处本应有一个WriteHandler的,确实,我们在ReadHandler中,以一个匿名内部类实现了它。
下面看客户端代码。
3.2、Client端代码
Client:
- package com.anxpp.io.calculator.aio.client;
- import java.util.Scanner;
- public class Client {
- private static String DEFAULT_HOST = "127.0.0.1";
- private static int DEFAULT_PORT = 12345;
- private static AsyncClientHandler 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 AsyncClientHandler(ip,port);
- new Thread(clientHandle,"Client").start();
- }
- //向服务器发送消息
- public static boolean sendMsg(String msg) throws Exception{
- if(msg.equals("q")) return false;
- clientHandle.sendMsg(msg);
- return true;
- }
- @SuppressWarnings("resource")
- public static void main(String[] args) throws Exception{
- Client.start();
- System.out.println("请输入请求消息:");
- Scanner scanner = new Scanner(System.in);
- while(Client.sendMsg(scanner.nextLine()));
- }
- }
AsyncClientHandler:
- package com.anxpp.io.calculator.aio.client;
- import java.io.IOException;
- 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 AsyncClientHandler implements CompletionHandler<Void, AsyncClientHandler>, Runnable {
- private AsynchronousSocketChannel clientChannel;
- private String host;
- private int port;
- private CountDownLatch latch;
- public AsyncClientHandler(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, this);
- try {
- latch.await();
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
- try {
- clientChannel.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //连接服务器成功
- //意味着TCP三次握手完成
- @Override
- public void completed(Void result, AsyncClientHandler attachment) {
- System.out.println("客户端成功连接到服务器...");
- }
- //连接服务器失败
- @Override
- public void failed(Throwable exc, AsyncClientHandler attachment) {
- System.err.println("连接服务器失败...");
- exc.printStackTrace();
- try {
- clientChannel.close();
- latch.countDown();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //向服务器发送消息
- public void sendMsg(String msg){
- byte[] req = msg.getBytes();
- ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
- writeBuffer.put(req);
- writeBuffer.flip();
- //异步写
- clientChannel.write(writeBuffer, writeBuffer,new WriteHandler(clientChannel, latch));
- }
- }
WriteHandler:
- package com.anxpp.io.calculator.aio.client;
- import java.io.IOException;
- import java.nio.ByteBuffer;
- import java.nio.channels.AsynchronousSocketChannel;
- import java.nio.channels.CompletionHandler;
- import java.util.concurrent.CountDownLatch;
- public class WriteHandler implements CompletionHandler<Integer, ByteBuffer> {
- private AsynchronousSocketChannel clientChannel;
- private CountDownLatch latch;
- public WriteHandler(AsynchronousSocketChannel clientChannel,CountDownLatch latch) {
- this.clientChannel = clientChannel;
- this.latch = latch;
- }
- @Override
- public void completed(Integer result, ByteBuffer buffer) {
- //完成全部数据的写入
- if (buffer.hasRemaining()) {
- clientChannel.write(buffer, buffer, this);
- }
- else {
- //读取数据
- ByteBuffer readBuffer = ByteBuffer.allocate(1024);
- clientChannel.read(readBuffer,readBuffer,new ReadHandler(clientChannel, latch));
- }
- }
- @Override
- public void failed(Throwable exc, ByteBuffer attachment) {
- System.err.println("数据发送失败...");
- try {
- clientChannel.close();
- latch.countDown();
- } catch (IOException e) {
- }
- }
- }
ReadHandler:
- package com.anxpp.io.calculator.aio.client;
- 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.concurrent.CountDownLatch;
- public class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
- private AsynchronousSocketChannel clientChannel;
- private CountDownLatch latch;
- public ReadHandler(AsynchronousSocketChannel clientChannel,CountDownLatch latch) {
- this.clientChannel = clientChannel;
- this.latch = latch;
- }
- @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();
- latch.countDown();
- } catch (IOException e) {
- }
- }
- }
这个API使用起来真的是很顺手。
3.3、测试
Test:
- package com.anxpp.io.calculator.aio;
- import java.util.Scanner;
- import com.anxpp.io.calculator.aio.client.Client;
- import com.anxpp.io.calculator.aio.server.Server;
- /**
- * 测试方法
- * @author yangtao__anxpp.com
- * @version 1.0
- */
- public class Test {
- //测试主方法
- @SuppressWarnings("resource")
- public static void main(String[] args) throws Exception{
- //运行服务器
- Server.start();
- //避免客户端先于服务器启动前执行代码
- Thread.sleep(100);
- //运行客户端
- Client.start();
- System.out.println("请输入请求消息:");
- Scanner scanner = new Scanner(System.in);
- while(Client.sendMsg(scanner.nextLine()));
- }
- }
我们可以在控制台输入我们需要计算的算数字符串,服务器就会返回结果,当然,我们也可以运行大量的客户端,都是没有问题的,以为此处设计为单例客户端,所以也就没有演示大量客户端并发。
读者可以自己修改Client类,然后开辟大量线程,并使用构造方法创建很多的客户端测试。
下面是其中一次参数的输出:
- 服务器已启动,端口号:12345
- 请输入请求消息:
- 客户端成功连接到服务器...
- 连接的客户端数:1
- 123456+789+456
- 服务器收到消息: 123456+789+456
- 客户端收到结果:124701
- 9526*56
- 服务器收到消息: 9526*56
- 客户端收到结果:533456
- ...
AIO是真正的异步非阻塞的,所以,在面对超级大量的客户端,更能得心应手。
下面就比较一下,几种I/O编程的优缺点。
4、各种I/O的对比
先以一张表来直观的对比一下:
具体选择什么样的模型或者NIO框架,完全基于业务的实际应用场景和性能需求,如果客户端很少,服务器负荷不重,就没有必要选择开发起来相对不那么简单的NIO做服务端;相反,就应考虑使用NIO或者相关的框架了。
5、附录
上文中服务端使用到的用于计算的工具类:
- package com.anxpp.utils;
- import javax.script.ScriptEngine;
- import javax.script.ScriptEngineManager;
- import javax.script.ScriptException;
- public final class Calculator {
- private final static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
- public static Object cal(String expression) throws ScriptException{
- return jse.eval(expression);
- }
- }
- AIO编程(NIO2.0)及代码实现
- java网络编程之AIO/NIO2.0(三)
- java中IO、NIO、AIO(NIO2.0)
- Java中的BIO、NIO、AIO(NIO2)
- Java中的BIO、NIO、AIO(NIO2)
- Java中的BIO、NIO、AIO(NIO2)
- Java中的BIO、NIO、AIO(NIO2)
- java中IO、NIO、AIO(NIO2.0)的学习
- Java高级篇(三九)------BIO、NIO、AIO(NIO2)区别
- NIO2.0
- Java NIO2 AIO开辟核心流程
- AIO编程
- AIO编程
- BIO编程 及实现代码
- NIO 编程及代码实现
- nio2
- nio2
- Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)
- Java(1-3)
- T
- Java 泛型总结(二):泛型与数组
- axis2 WebService的发布与调用
- Jquery中$(document).ready()和window.onload的区别
- AIO编程(NIO2.0)及代码实现
- Android Studio 好用的快捷键之Eclipse模式
- Android:点击链接打开APP
- jquery validate添加自定义开始结束日期验证
- opencv AttributeError: 'module' object has no attribute 'CV_LOAD_IMAGE_COLOR'
- 什么是JDK,JRE,SDK,JVM以及API
- 管好你的时间只需要做到这3点
- 虚拟化笔记05 OpenFiler configuration
- Docker核心概念