aio demo

java aio 的一个demo

声明:本人小白一枚,如有不对的地方,欢迎一起讨论

aio 就是nio2.0,相对于nio1.0,系统帮我们做了更多的东西。让我们能够更多的注重业务代码实现。

服务端的实现

直接上代码,里面有注释

public static AsynchronousServerSocketChannel serverSocketChannel;//第二次接收的时候会使用到,因此作为一个属性
    
    public static void main(String[] args) {
        //新建一个线程池,使得aio中的操作都使用这个线程池中的线程,而且,还传入一个ThreadFactory对象,自己去定义线程
        ExecutorService executorService = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        try {
            //创建使用的公共线程池资源
            AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executorService);
            //创建AsynchronousServerSocketChannel对象
            if(serverSocketChannel==null){
                serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
            }
            //设定一些参数
            //接收缓冲区的大小
            serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);
            //是否重用本地地址
            serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
            //绑定监听的端口号
            serverSocketChannel.bind(new InetSocketAddress(Const.PORT));
            //开始接收请求,第一个参数是根据自己的需求出入对应的对象
            //第二个参数CompletionHandler的子对象
            serverSocketChannel.accept(new AioServer(), new MyCompletion());
            System.out.println("服务启动...");
            //保持程序不停止
            waitRevicer();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }


accept 方法中第二个参数的说明

这里对MyCompletion进行说明,它是CompletionHandler的实现类,实现了父类的completed方法和failed方法。

completed 方法是接收客户端的数据并进行处理。

failed 方法是程序发生了错误,对错误信息进行处理,本demo只是进行错误信息打印,故不进行解释说明。

completed

是接收完一个请求,对请求进行处理。有两个参数,第一个是客户端请求的对象的实例,第二个参数是我们在accept方法中传入的对象。 通过客户端对象的read方法,我们能够获取客户端发送的数据。下面直接上代码,下面涉及到了一些ByteBuffer类的知识,不会的同学,请移步aio系列文档(2)----图解bytebuffer,一个不错的文章。

public void completed(AsynchronousSocketChannel result, AioServer attachment) {
        System.out.println("completed");
        //用来缓存数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            //读取客户端数据,存放到ByteBuffer对象中
            result.read(byteBuffer).get();
            //重新设置position和limit的值,为读取值做准备
            byteBuffer.flip();
            //根据值的长度创建对象,并将ByteBuffer中的数据存入,打印输出
            byte[] input = new byte[byteBuffer.remaining()];
            byteBuffer.get(input);
            System.out.println(new String(input));
            //重新设置position的值,并将客户端发送的值返回给客户端
            byteBuffer.position(0);
            result.write(byteBuffer);
//          String sayHello = new String("你好啊,客户端"+result.getRemoteAddress().toString());
//          System.out.println("sayHello:"+sayHello);
//          result.write(ByteBuffer.wrap(sayHello.getBytes("UTF-8")));
            attachment.serverSocketChannel.accept(attachment, this);//不重新设置接收的话就只能接收一次了
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
    }

需要注意的是在读取客户端数据的时候,因为ByteBuffer的长度是1024,所以最多只能读取1024个字节的数据,没有把所有的数据取完,下面客户端的代码有这个实现,可以参考下。

客户端的实现

上代码

    //获取AsynchronousSocketChannel的实例,这里可以跟服务器端一样传入一个AsynchronousChannelGroup的对象
    AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
    //连接服务器
    Future<Void> future = socketChannel.connect(new InetSocketAddress(Const.SERVER_IP,Const.PORT));
    //注意,这个get是一个阻塞的方法,只有当连接上了之后才会往下走
    //如果不执行get方法,等程序没有连接成功时进行发送数据的操作会发生错误
    future.get();
    //拼接发送给服务器的数据
    Random rom = new Random();
    StringBuffer writeBuf = new StringBuffer();
    writeBuf.append("this is client").append(rom.nextInt(1000));
    System.out.println(writeBuf.toString());
    ByteBuffer byteBuffer = ByteBuffer.wrap(writeBuf.toString().getBytes());
    //发送数据
    socketChannel.write(byteBuffer);
    //读取服务器的返回的数据
    read(socketChannel);

读取数据的方法基本同服务端是一致的,只是加了一个循环,直到取出所有的数据,不过多解释

    private void read(AsynchronousSocketChannel socketChannel){
        ByteBuffer byteBufer = ByteBuffer.allocate(10);
        try {
            StringBuffer strBuf = new StringBuffer("客户端:");
            boolean hasData = true;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while(hasData){
                socketChannel.read(byteBufer).get();
                if(byteBufer.capacity()>byteBufer.position()){//当容器大小大于容器里面的数量的时候,认为读取完毕
                    hasData = false;
                }
                byteBufer.flip();
                byte[] buf = new byte[byteBufer.remaining()];
                byteBufer.get(buf);
                if(hasData){
                    System.out.println("数据没有读取完毕继续读取");
                }else{
                    System.out.println("数据读取完毕");
                }
                out.write(buf);
                byteBufer.clear();
            }
            strBuf.append(new String(out.toByteArray(),"UTF-8"));
            System.out.println(strBuf);
            read(socketChannel);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

完整的代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * aio 服务端
 * @author zxr
 * 2017年8月10日 下午4:23:02
 */
public class AioServer {
    
    public static AsynchronousServerSocketChannel serverSocketChannel;//第二次接收的时候会使用到,因此作为一个属性
    
    public static void main(String[] args) {
        //新建一个线程池,使得aio中的操作都使用这个线程池中的线程,而且,还传入一个ThreadFactory对象,自己去定义线程
        ExecutorService executorService = new ThreadPoolExecutor(10, 10, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
        try {
            //创建使用的公共线程池资源
            AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executorService);
            //创建AsynchronousServerSocketChannel对象
            if(serverSocketChannel==null){
                serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
            }
            //设定一些参数
            //接收缓冲区的大小
            serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);
            //是否重用本地地址
            serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
            //绑定监听的端口号
            serverSocketChannel.bind(new InetSocketAddress(Const.PORT));
            //开始接收请求,第一个参数是根据自己的需求出入对应的对象
            //第二个参数CompletionHandler的子对象
            serverSocketChannel.accept(new AioServer(), new MyCompletion());
            System.out.println("服务启动...");
            //保持程序不停止
            waitRevicer();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    public static void waitRevicer(){
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                while(true){
                    System.out.println("保持程序运行不停止...");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

}

class MyCompletion implements CompletionHandler<AsynchronousSocketChannel,AioServer>{

    @Override
    public void completed(AsynchronousSocketChannel result, AioServer attachment) {
        System.out.println("completed");
        //用来缓存数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            //读取客户端数据,存放到ByteBuffer对象中
            result.read(byteBuffer).get();
            //重新设置position和limit的值,为读取值做准备
            byteBuffer.flip();
            //根据值的长度创建对象,并将ByteBuffer中的数据存入,打印输出
            byte[] input = new byte[byteBuffer.remaining()];
            byteBuffer.get(input);
            System.out.println(new String(input));
            //重新设置position的值,并将客户端发送的值返回给客户端
            byteBuffer.position(0);
            result.write(byteBuffer);
//          String sayHello = new String("你好啊,客户端"+result.getRemoteAddress().toString());
//          System.out.println("sayHello:"+sayHello);
//          result.write(ByteBuffer.wrap(sayHello.getBytes("UTF-8")));
            attachment.serverSocketChannel.accept(attachment, this);//不重新设置接收的话就只能接收一次了
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
    }

    @Override
    public void failed(Throwable exc, AioServer attachment) {
        System.out.println("failed");
        System.out.println(attachment);
        System.out.println(exc);
        
    }
    
}
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class AioClient {
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        new AioClientThread().start();
        new AioClientThread().start();
        new AioClientThread().start();
    }

}

class AioClientThread extends Thread{
    
    @Override
    public void run() {
        super.run();
        try {
            //获取AsynchronousSocketChannel的实例,这里可以跟服务器端一样传入一个AsynchronousChannelGroup的对象
            AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
            //连接服务器
            Future<Void> future = socketChannel.connect(new InetSocketAddress(Const.SERVER_IP,Const.PORT));
            //注意,这个get是一个阻塞的方法,只有当连接上了之后才会往下走
            //如果不执行get方法,等程序没有连接成功时进行发送数据的操作会发生错误
            future.get();
            //拼接发送给服务器的数据
            Random rom = new Random();
            StringBuffer writeBuf = new StringBuffer();
            writeBuf.append("this is client").append(rom.nextInt(1000));
            System.out.println(writeBuf.toString());
            ByteBuffer byteBuffer = ByteBuffer.wrap(writeBuf.toString().getBytes());
            //发送数据
            socketChannel.write(byteBuffer);
            //读取服务器的返回的数据
            read(socketChannel);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
    
    private void read(AsynchronousSocketChannel socketChannel){
        ByteBuffer byteBufer = ByteBuffer.allocate(10);
        try {
            StringBuffer strBuf = new StringBuffer("客户端:");
            boolean hasData = true;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while(hasData){
                socketChannel.read(byteBufer).get();
                if(byteBufer.capacity()>byteBufer.position()){//当容器大小大于容器里面的数量的时候,认为读取完毕
                    hasData = false;
                }
                byteBufer.flip();
                byte[] buf = new byte[byteBufer.remaining()];
                byteBufer.get(buf);
                if(hasData){
                    System.out.println("数据没有读取完毕继续读取");
                }else{
                    System.out.println("数据读取完毕");
                }
                out.write(buf);
                byteBufer.clear();
            }
            strBuf.append(new String(out.toByteArray(),"UTF-8"));
            System.out.println(strBuf);
            read(socketChannel);
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

public class Const {
    
    public static final int PORT = 6987;
    
    public static final String SERVER_IP = "127.0.0.1";

}

猜你喜欢

转载自my.oschina.net/u/2523704/blog/1523661
今日推荐