NIO、AIO

day13-NIO、AIO

今日目标:
	NIO
    AIO         

第一章 Java中各种IO的概述

1.阻塞与非阻塞
阻塞: 当调用某个方法时,方法任务没有结束,不会返回结果,程序无法继续执行
非阻塞: 当调用某个方法时,无论方法任务是否完成,直接返回,后续可以通过其他条件判断任务是否完成

2.同步与异步

同步: 当调用某个方法时,可以等待该方法结束后再返回,也可以直接返回,后续通过其他条件判断任务是否完成
异步: 当调用某个方法时,无论方法任务是否完成,直接返回,后续不需要自己判断,而是任务完成之后会通知我们
3.BIO,NIO,AIO的介绍
BIO:同步阻塞的IO(传统的IO流)
NIO:同步非阻塞的IO(也可以是同步阻塞的)
    NIO中主要有三大类: 缓冲区,通道,选择器
AIO:异步的IO    
    回调函数

第二章 NIO-Buffer类(了解)

1.Buffer的介绍和种类
  • 什么是Buffer

    就是NIO中的缓冲区,作用是存储临时数据的地方,其本质就是一个数组
    
  • Buffer的一般操作步骤

    1.写入数据到Buffer中
    2.调用flip()方法(把Buffer切换成读模式)
    3.从Buffer中读取数据 
    4.调用clear()方法或者compact()方法(清空缓冲区,并切换成写模式)    
    
  • Buffer的种类

    ByteBuffer[最常用的]
    CharBuffer
    DoubleBuffer
    FloatBuffer
    IntBuffer
    LongBuffer
    ShortBuffer
    
2.ByteBuffer的三种创建方式
public class BufferDemo01 {
    public static void main(String[] args) {
        //1.在堆区申请空间(间接缓冲区)
        ByteBuffer b1 = ByteBuffer.allocate(10);
        System.out.println(Arrays.toString(b1.array()));
        //2.在系统内容中申请空间(直接缓冲区)
        ByteBuffer b2 = ByteBuffer.allocateDirect(20);
        System.out.println(b2);
        //间接缓冲区创建和销毁效率高于直接缓冲区
        //间接缓冲区的操作效率低于直接缓冲区
        //3.间接缓冲区
        byte[] bs = new byte[10];
        ByteBuffer b3 = ByteBuffer.wrap(bs);
    }
}
3.ByteBuffer的三种添加数据方式
public class BufferDemo02 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.添加数据
        //a.一次加一个
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        System.out.println(Arrays.toString(buffer.array()));
        //b.一次加一个数组
        byte[] bs = {40,50,60};
        buffer.put(bs);
        System.out.println(Arrays.toString(buffer.array()));
        //c.一次加一个数组的一部分
        byte[] bs1 = {70,80,90};
        buffer.put(bs1,1,2);
        System.out.println(Arrays.toString(buffer.array()));
    }
}
4.ByteBuffer的容量-capacity
Buffer的容量(capacity)是指:Buffer所能够包含的元素的最大数量
    
public class BufferDemo03 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.获取容量
        System.out.println("容量是:"+buffer.capacity()); -- 获取容量
        //3.添加
        buffer.put((byte)10);
        buffer.put((byte)20);
        //4.再次获取容量
        System.out.println("容量是:"+buffer.capacity()); -- 获取容量
    }
}

5.ByteBuffer的限制-limit
限制limit是指:第一个不应该读取或写入元素的index索引

public class BufferDemo04 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(15);
        //2.查看限制
        System.out.println("限制:"+buffer.limit());
        //3.修改限制
        buffer.limit(5);
        System.out.println("限制:"+buffer.limit());
        //4.添加数据
        buffer.put((byte) 10);
        buffer.put((byte) 10);
        buffer.put((byte) 10);
        buffer.put((byte) 10);
        buffer.put((byte) 10);
        System.out.println(Arrays.toString(buffer.array()));
        //5.再次添加,报错,因为限制是5,也就说5索引开始不能添加数据了
        buffer.put((byte) 10);
    }
}    
6.ByteBuffer的位置-position
位置position是指:当前要操作的元素索引(位置必须大于等于0,且小于等于限制)
    
public class BufferDemo05 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.查看位置
        System.out.println("当前位置:" + buffer.position());
        //3.添加
        buffer.put((byte) 10);
        System.out.println(Arrays.toString(buffer.array()));
        //3.再次查看位置
        System.out.println("当前位置:" + buffer.position());
        //4.再次添加
        buffer.put((byte) 20);
        System.out.println(Arrays.toString(buffer.array()));
        //5.修改位置
        buffer.position(5);
        buffer.put((byte) 30);
        System.out.println(Arrays.toString(buffer.array()));
        //6.继续添加
        buffer.put((byte)40);
        buffer.put((byte)50);
        buffer.put((byte)60);
        buffer.put((byte)70);
        System.out.println(Arrays.toString(buffer.array()));
        System.out.println("当前位置:" + buffer.position());
        //注意:位置可以等于限制,但是等于限制时,不能操作当前位置的元素
    }
}    
7.ByteBuffer的标记-mark
ByteBuffer的标记-mark: 做一个标记(mark = position),当调用reset方法时,(position = mark)
 
public class BufferDemo06 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.添加元素
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        //3.当前位置是3,做一个标记
        buffer.mark();
        //4.添加元素
        buffer.put((byte)40);
        buffer.put((byte)50);
        buffer.put((byte)60);
        System.out.println(Arrays.toString(buffer.array()));
        //5.重置缓冲区
        buffer.reset();
        buffer.put((byte)70);
        buffer.put((byte)80);
        System.out.println(Arrays.toString(buffer.array()));
    }
}       
8. ByteBuffer的其他方法
public void clear(); 还原缓冲区的状态
    a.将position设置为:0
	b.将限制limit设置为容量
	c.丢弃标记mark

public class BufferDemo07 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前没有标记");
        //2.添加数据
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        buffer.put((byte)40);
        buffer.limit(6);
        buffer.mark();
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前有标记");
        //3.clear
        System.out.println("====clear方法===");
        buffer.clear();
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前没有标记");
    }
}

public Buffer flip():缩小limit的范围(理解成切换模式,把写模式切换读模式)
	a.将limit设置为当前position位置    
	b.将当前position位置设置为0
    c.丢弃标记
 
public class BufferDemo08 {
    public static void main(String[] args) {
        //1.创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前没有标记");
        //2.添加数据
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        buffer.put((byte)40);
        buffer.mark();
        //3.打印
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前有标记");
        //4.flip
        System.out.println("=====flip()====");
        buffer.flip();
        System.out.println("当前位置:"+buffer.position());
        System.out.println("当前限制:"+buffer.limit());
        System.out.println("当前没有标记");
    }
}
public Buffer rewind():重绕此缓冲区。
    a.把position置为0
    b.limit不变
    c.丢弃标记

第三章 Channel(通道)(了解)

1. Channel介绍和分类
Channel称为通道,用于读或者写数据,和IO流相似,但是不同的是IO流是单向的,而通道是双向的
 
Channel的分类    
FileChannel:文件通道
DatagramChannel:基于UDP协议数据传输的通道
SocketChannel:基于TCP协议中客户端的通道
ServerSocketChannel:基于TCP协议中服务器端的通道        
2. FileChannel类的基本使用
public class FileChannelDemo {
    public static void main(String[] args) throws IOException {
        //1.创建2个流
        FileInputStream fis = new FileInputStream("1.png");
        FileOutputStream fos = new FileOutputStream("copy.png");
        //2.获取通道
        FileChannel channelIn = fis.getChannel();
        FileChannel channelOut = fos.getChannel();
        //3.复制文件
        ByteBuffer buffer = ByteBuffer.allocate(1024); //position = 0,limit = 1024
        int eof = 0;
        while((eof =channelIn.read(buffer)) != -1){ // position = 1024,limit = 1024
            //切换成读模式
            buffer.flip(); // position = 0 limit = 1024
            //开始写
            channelOut.write(buffer);
            //清空
            buffer.clear(); //position = 0 limit = 1024 丢弃标记
        }
        channelOut.close();
        channelIn.close();
        fos.close();
        fis.close();
    }
}
3. FileChannel结合MappedByteBuffer实现高效读写
public class FileChannelAndMappedByteBufferDemo {
    public static void main(String[] args) throws IOException {
        //1.创建两个文件的对象
        RandomAccessFile source = new RandomAccessFile("1.png", "r"); // r表示只读模式。
        RandomAccessFile target = new RandomAccessFile("copy.png", "rw"); //rw表示可读可写
        //2.获取通道
        FileChannel inChannel = source.getChannel();
        FileChannel outChannel = target.getChannel();
        //3.映射缓冲区(镜像)
        long size = inChannel.size();
        MappedByteBuffer mbbi = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer mbbo = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
        //4.复制
        System.out.println("开始...");
        long start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            byte b = mbbi.get(i);//读取一个字节
            mbbo.put(i, b);//将字节添加到mbbo中
        }
        long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - start) + " 毫秒");
        //5.释放资源
        outChannel.close();
        inChannel.close();
        target.close();
        source.close();
    }
}
注意: 使用MappedByteBuffer进行文件复制时,要求文件的大小不能操作2GB
    	如果文件的大小在2GB以上,使用分块进行映射复制
4. SocketChannel和ServerSocketChannel的实现连接
  • C/S阻塞模式

    ==================NIO使用同步阻塞的===================
    SocketChannel 其实就是我们以前用的Socket
    public class SocketChannelDemo01 {
        public static void main(String[] args) throws IOException {
            //1.开启一个客户端通道
            SocketChannel channel = SocketChannel.open();
            //2.连接服务器
            channel.connect(new InetSocketAddress("127.0.0.1", 8888));
        }
    }
    
    ServerSocketChannel 其实就是我们以前用的ServerSocket
    public class ServerSocketChannelDemo01 {
        public static void main(String[] args) throws IOException {
            //1.开启服务器通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //2.绑定本地的端口号
            serverSocketChannel.bind(new InetSocketAddress(8888));
            //3.接收客户端通道
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("有客户端连接了...");
        }
    }
    
  • C/S非阻塞模式

    ==================NIO使用同步非阻塞的===================
    //非阻塞的客户端    
    public class SocketChannelDemo02 {
        public static void main(String[] args) throws IOException {
            //1.开启一个客户端通道
            SocketChannel channel = SocketChannel.open();
            //2.连接服务器
            channel.configureBlocking(false); // 开启NIO的非阻塞功能
            channel.connect(new InetSocketAddress("127.0.0.1", 8888));
            System.out.println("程序继续执行...");
        }
    }
        
    
    //非阻塞的服务器
    public class ServerSocketChannelDemo02 {
        public static void main(String[] args) throws IOException {
            //1.开启服务器通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //2.绑定本地的端口号
            serverSocketChannel.bind(new InetSocketAddress(8888));
            //3.接收客户端通道
            serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel == null) {
                System.out.println("无客户端");
            }else{
                System.out.println("有客户端来了..");
            }
        }
    }
    
  • 使用C阻塞模式和S非阻塞模式,实现连接

     public class SocketChannelDemo03 {
        public static void main(String[] args){
            while (true) {
                try {
                    //1.开启一个客户端通道
                    SocketChannel channel = SocketChannel.open();
                    //2.连接服务器
                    channel.connect(new InetSocketAddress("127.0.0.1", 8888));
                    System.out.println("连接成功...");
    
    
    
                    break;
                } catch (IOException ie) {
                    System.out.println("连接失败..3秒后重试..");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    public class ServerSocketChannelDemo03 {
        public static void main(String[] args) throws IOException {
            //1.开启服务器通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //2.绑定本地的端口号
            serverSocketChannel.bind(new InetSocketAddress(8888));
            //3.接收客户端通道
            serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能
            while (true) {
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    System.out.println("有客户端来了..");
    
    
                    break;
                } else {
                    System.out.println("暂时无客户端,等待3秒...");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
5. SocketChannel和ServerSocketChannel的实现通信
public class SocketChannelDemo04 {
    public static void main(String[] args){
        while (true) {
            try {
                //1.开启一个客户端通道
                SocketChannel channel = SocketChannel.open();
                //2.连接服务器
                channel.connect(new InetSocketAddress("127.0.0.1", 8888));
                System.out.println("连接成功...");
                //3.给服务器发送数据
                byte[] bs = "你好,我是NIO客户端".getBytes();
                ByteBuffer buffer = ByteBuffer.wrap(bs);
                channel.write(buffer);
                //4.接收服务器的回复
                ByteBuffer buffer1 = ByteBuffer.allocate(100);
                channel.read(buffer1);
                buffer1.flip();
                System.out.println("客户端说:"+new String(buffer1.array(),0,buffer1.limit()));
                //5.释放资源
                channel.close();
                break;
            } catch (IOException ie) {
                System.out.println("连接失败..3秒后重试..");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class ServerSocketChannelDemo04 {
    public static void main(String[] args) throws IOException {
        //1.开启服务器通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.绑定本地的端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.接收客户端通道
        serverSocketChannel.configureBlocking(false);//开启NIO的同步非阻塞功能
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                System.out.println("有客户端来了..");
                //4.接收客户端发送的数据
                ByteBuffer buffer = ByteBuffer.allocate(100);
                socketChannel.read(buffer);
                buffer.flip();
                System.out.println("客户端说:"+new String(buffer.array(),0,buffer.limit()));
                //5.回数据
                byte[] bs = "你也好,我是NIO的服务器".getBytes();
                ByteBuffer buffer1 = ByteBuffer.wrap(bs);
                socketChannel.write(buffer1);
                //6.释放资源
                socketChannel.close();
                serverSocketChannel.close();
                break;
            } else {
                System.out.println("暂时无客户端,等待3秒...");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

第四章 Select(选择器)(了解)

4.1 多路复用器(Selector)的概念
如果不使用多路复用器,那么每个服务器通道都需要开启一个线程
如果使用多路复用器,那么多个服务器通道我们只需要注册到多路复用器中,开启一个线程即可       
4.2 选择器Selector_服务器端实现多路注册
创建一个选择器:
	Selector selector = Selector.open();
注册Channel到Selector中:
	channel.configureBlocking(false); -- 设置通道为非阻塞
	SelectionKey key = channel.register(selector,SelectionKey.OP_ACCEPT);

public class SelectorDemo01 {
    public static void main(String[] args) throws IOException {
        //1.创建三个服务器通道
        ServerSocketChannel serverSocketChannel1 = ServerSocketChannel.open();
        serverSocketChannel1.configureBlocking(false);
        serverSocketChannel1.bind(new InetSocketAddress(7777));

        ServerSocketChannel serverSocketChannel2 = ServerSocketChannel.open();
        serverSocketChannel2.configureBlocking(false);
        serverSocketChannel2.bind(new InetSocketAddress(8888));

        ServerSocketChannel serverSocketChannel3 = ServerSocketChannel.open();
        serverSocketChannel3.configureBlocking(false);
        serverSocketChannel3.bind(new InetSocketAddress(9999));

        //2.创建一个多路复用器
        Selector selector = Selector.open();

        //3.将三个通道注册到选择器中
        serverSocketChannel1.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannel2.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannel3.register(selector, SelectionKey.OP_ACCEPT);
    }
}
4.3 Selector中的常用方法
public Set<SelectionKey> keys(); 返回集合,表示已经注册到本选择器所有服务器信息封装后对象
    
public Set<SelectionKey> selectedKeys();返回集合,表示本选择器中已经被客户端连接所有服务器信息封装后对象    
public int select(); 此方法返回一共有多少个服务器通道被连接,如果没有,则阻塞       
4.4.Selector实现多路连接
public class SelectorDemo02 {
    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("7777客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 7777));
                    System.out.println("7777客户端连接成功....");
                    break;
                } catch (IOException e) {
                    System.out.println("7777异常重连");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }).start();
        new Thread(() -> {
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("8888客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 8888));
                    System.out.println("8888客户端连接成功....");
                    break;
                } catch (IOException e) {
                    System.out.println("8888异常重连");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
public class SelectorDemo03 {
    public static void main(String[] args) throws IOException, InterruptedException {
        ServerSocketChannel channelA = ServerSocketChannel.open();
        channelA.configureBlocking(false);
        channelA.bind(new InetSocketAddress(7777));

        ServerSocketChannel channelB = ServerSocketChannel.open();
        channelB.configureBlocking(false);
        channelB.bind(new InetSocketAddress(8888));

        ServerSocketChannel channelC = ServerSocketChannel.open();
        channelC.configureBlocking(false);
        channelC.bind(new InetSocketAddress(9999));
        //获取选择器
        Selector selector = Selector.open();
        //注册三个通道
        channelA.register(selector, SelectionKey.OP_ACCEPT);
        channelB.register(selector, SelectionKey.OP_ACCEPT);
        channelC.register(selector, SelectionKey.OP_ACCEPT);
        //打印一些数据
        //获取已注册通道的集合
        Set<SelectionKey> keys = selector.keys();
        System.out.println("注册通道数量:" + keys.size());
        System.out.println("-------------");
        //获取已连接通 道的集合
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        System.out.println("已连接的通道数量:" + selectionKeys.size());
        System.out.println("-------------");
        System.out.println("【服务器】等待连接......");
        //此方法会"阻塞"
        int selectedCount = selector.select();
        System.out.println("连接数量:" + selectedCount);
        System.out.println("-------------");
        Set<SelectionKey> keys1 = selector.keys();
        System.out.println("注册通道数量:" + keys1.size());
        Set<SelectionKey> selectionKeys1 = selector.selectedKeys();
        System.out.println("已连接的通道数量:" + selectionKeys1.size());
    }
}

4.4 Selector多路信息接收测试
public class SelectorDemo04 {
    public static void main(String[] args) {
        //两个线程,模拟两个客户端,分别连接服务器的7777,8888端口
        new Thread(() -> {
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("7777客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 7777));
                    System.out.println("7777客户端连接成功....");
                    //发送信息
                    ByteBuffer outBuf = ByteBuffer.allocate(100);
                    outBuf.put("我是客户端,连接7777端口".getBytes());
                    outBuf.flip();
                    socket.write(outBuf);
                    break;
                } catch (IOException e) {
                    System.out.println("7777异常重连");
                }
            }
        }).start();
        new Thread(() -> {
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("8888客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 8888));
                    System.out.println("8888客户端连接成功....");
                    //发送信息
                    ByteBuffer outBuf = ByteBuffer.allocate(100);
                    outBuf.put("我是客户端,连接8888端口".getBytes());
                    outBuf.flip();
                    socket.write(outBuf);
                    break;
                } catch (IOException e) {
                    System.out.println("8888异常重连");
                }
            }
        }).start();


        new Thread(() -> {
            while (true) {
                try (SocketChannel socket = SocketChannel.open()) {
                    System.out.println("9999客户端连接服务器......");
                    socket.connect(new InetSocketAddress("localhost", 9999));
                    System.out.println("9999客户端连接成功....");
                    //发送信息
                    ByteBuffer outBuf = ByteBuffer.allocate(100);
                    outBuf.put("我是客户端,连接9999端口".getBytes());
                    outBuf.flip();
                    socket.write(outBuf);
                    break;
                } catch (IOException e) {
                    System.out.println("9999异常重连");
                }
            }
        }).start();
    }
}

public class SelectorDemo05 {
    public static void main(String[] args) throws IOException {
        //1.同时监听三个端口:7777,8888,9999
        ServerSocketChannel serverChannel1 = ServerSocketChannel.open();
        serverChannel1.bind(new InetSocketAddress(7777));
        serverChannel1.configureBlocking(false);
        ServerSocketChannel serverChannel2 = ServerSocketChannel.open();
        serverChannel2.bind(new InetSocketAddress(8888));
        serverChannel2.configureBlocking(false);
        ServerSocketChannel serverChannel3 = ServerSocketChannel.open();
        serverChannel3.bind(new InetSocketAddress(9999));
        serverChannel3.configureBlocking(false);
        //2.获取一个选择器
        Selector selector = Selector.open();
        //3.注册三个通道
        SelectionKey key1 = serverChannel1.register(selector, SelectionKey.OP_ACCEPT);
        SelectionKey key2 = serverChannel2.register(selector, SelectionKey.OP_ACCEPT);
        SelectionKey key3 = serverChannel3.register(selector, SelectionKey.OP_ACCEPT);
        //4.循环
        while (true) {
            System.out.println("等待客户端连接...");
            int keyCount = selector.select();
            System.out.println("连接数量:" + keyCount);
            //遍历已连接的每个通道的SelectionKey
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> it = keys.iterator();
            while (it.hasNext()) {
                SelectionKey nextKey = it.next();
                System.out.println("获取通道...");
                ServerSocketChannel channel = (ServerSocketChannel) nextKey.channel();

                System.out.println("等待【" + channel.getLocalAddress() + "】 通道数据...");
                SocketChannel socketChannel = channel.accept();
                //接收数据
                ByteBuffer inBuf = ByteBuffer.allocate(100);
                socketChannel.read(inBuf);
                inBuf.flip();
                String msg = new String(inBuf.array(), 0, inBuf.limit());
                System.out.println("【服务器】接收到通道【" + channel.getLocalAddress() + "】的信息:" + msg);
                //移除该通道
                it.remove();
            }
        }
    }
}

第五章 AIO(异步、非阻塞)(了解)

5.1 AIO概述
AIO:异步的IO
    当调用一个方法时,不需要等待方法任务结束,直接返回,当方法任务结束后,会回调函数通知我们我们
5.2 AIO 异步非阻塞连接
public class AIODemo01 {
    public static void main(String[] args) throws IOException {
        //1.异步的客户端通道
        AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open();

        //2.连接服务器
        asynchronousSocketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Void>() {

            @Override
            public void completed(Void result, Void attachment) {
                System.out.println("连接成功...");
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("连接失败...");
            }
        });
        System.out.println("程序继续...");
    }
}

public class AIODemo02 {
    public static void main(String[] args) throws IOException {
        //1.创建异步服务器通道
        AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
        //2.绑定端口
        asynchronousServerSocketChannel.bind(new InetSocketAddress(8888));
        //3.接收客户端
        asynchronousServerSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Void attachment) {
                System.out.println("连接成功...");
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("连接失败...");
            }
        });
        System.out.println("服务器继续....");
    }
}
5.3 AIO同步非阻塞读写数据
public class AIODemo03 {
    public static void main(String[] args) throws InterruptedException, IOException {
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();

        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Void>() {
            @Override
            public void completed(Void result, Void attachment) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("客户端连接成功");
                Future<Integer> writeFuture = socketChannel.write(ByteBuffer.wrap("我来自客户 端...".getBytes()));
                //同步写
                try {
                    System.out.println("写入大小:" + writeFuture.get());
                    socketChannel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("客户端失败!");
            }
        });
        System.out.println("客户端继续");
        Thread.sleep(30000);
    }
}

public class AIODemo04 {
    public static void main(String[] args) throws InterruptedException, IOException {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();

        serverSocketChannel.bind(new InetSocketAddress(8888));
        //异步的accept()
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            //有客户端连接成功的回调函数
            @Override
            public void completed(AsynchronousSocketChannel result, Void attachment) {
                System.out.println("服务器端接收到连接...");
                ByteBuffer byteBuffer = ByteBuffer.allocate(30);
                Future<Integer> readFuture = result.read(byteBuffer);
                //同步读
                try {
                    System.out.println("读取信息:" + new String(byteBuffer.array(), 0, readFuture.get()));
                    result.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            //IO操作失败时的回调函数
            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("IO操作失败!");
            }
        });
        System.out.println("服务器端继续....");
        Thread.sleep(30000);
    }
}
5.4 AIO异步非阻塞读写
public class AIODemo05 {
    public static void main(String[] args) throws IOException, InterruptedException {
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();

        socketChannel.connect(new InetSocketAddress("localhost", 8888), null, new CompletionHandler<Void, Void>() {
            @Override
            public void completed(Void result, Void attachment) {

                socketChannel.write(ByteBuffer.wrap("你好服务器".getBytes()), null, new CompletionHandler<Integer, Void>() {
                    @Override
                    public void completed(Integer result, Void attachment) {
                        System.out.println("输出完毕!");
                    }

                    @Override
                    public void failed(Throwable exc, Void attachment) {
                        System.out.println("输出失败!");
                    }
                });
                try {
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                System.out.println("【客户端】异常!");
            }
        });
        System.out.println("客户端继续...");
        Thread.sleep(30000);
    }
}
public class AIODemo06 {
    public static void main(String[] args) throws InterruptedException, IOException {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));

        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel ch, Void attachment) {
//                serverSocketChannel.accept(null, this);
                ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.MAX_VALUE / 300);
                System.out.println("【服务器】read开始...");
                ch.read(byteBuffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Void>() {
                    @Override
                    public void completed(Integer result, Void attachment) {
                        if (result == -1) {
                            System.out.println("客户端没有传输数据就close了...");
                        }
                        System.out.println("服务器读取数据:" + new String(byteBuffer.array(), 0, result));
                        try {
                            ch.close();
                            System.out.println("服务器关闭!");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void failed(Throwable exc, Void attachment) {
                        exc.printStackTrace();
                        System.out.println(attachment);
                        System.out.println("【服务器】异常");
                    }
                });
                System.out.println("【服务器】read结束...");
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
            }
        });
        System.out.println("服务器开始循环...");
        Thread.sleep(30000);
    }
}

总结:
"能够说出同步和异步的概念
    同步: 可能是阻塞的,也可以是非阻塞
    异步: 一定是非阻塞的,并且调用方法时,不等待方法任务结束,直接返回,后期通过回调函数通知程序员   
    
"能够说出阻塞和非阻塞的概念
    阻塞: 调用方法时,等待方法任务结束才返回
    非阻塞: 调用方法时,不等待方法任务结束,直接返回,后期通过其他方式判断任务是否结束
       
    
能够创建和使用ByteBuffer        
能够使用MappedByteBuffer实现高效读写
能够使用ServerSocketChannel和SocketChannel实现连接并收发信息
能够说出Selector选择器的作用
能够使用Selector选择器
能够说出AIO的特点

猜你喜欢

转载自blog.csdn.net/qq_41371264/article/details/103864117
今日推荐