JavaSE(Selector选择器、NIO2-AIO异步和非阻塞)

Selector(选择器)

多路复用的概念

选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。

"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。

  • 服务器端的非多路复用效果
    在这里插入图片描述
    如果不使用“多路复用”,服务器端需要开很多线程处理每个端口的请求。如果在高并发环境下,造成系统性能下降。
  • 服务器端的多路复用效果
    在这里插入图片描述
    使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势

选择器Selector的获取和注册

Selector选择器的概述和作用

概述: Selector被称为:选择器,也被称为:多路复用器,可以把多个Channel注册到一个Selector选择器上, 那么就可以实现利用一个线程来处理这多个Channel上发生的事件,并且能够根据事件情况决定Channel读写。这样,通过一个线程管理多个Channel,就可以处理大量网络连接了, 减少系统负担, 提高效率。因为线程之间的切换对操作系统来说代价是很高的,并且每个线程也会占用一定的系统资源。所以,对系统来说使用的线程越少越好。

作用: 一个Selector可以监听多个Channel发生的事件, 减少系统负担 , 提高程序执行效率 .

Selector选择器的获取

Selector selector = Selector.open();

注册Channel到Selector

通过调用 channel.register(Selector sel, int ops)方法来实现注册:

channel.configureBlocking(false);// 设置非阻塞
SelectionKey key =channel.register(selector,SelectionKey.OP_READ);

register()方法的第二个参数:是一个int值,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件,而且可以使用SelectionKey的四个常量表示:

  1. 连接就绪–常量:SelectionKey.OP_CONNECT

  2. 接收就绪–常量:SelectionKey.OP_ACCEPT (ServerSocketChannel在注册时只能使用此项)

  3. 读就绪–常量:SelectionKey.OP_READ

  4. 写就绪–常量:SelectionKey.OP_WRITE

注意:对于ServerSocketChannel在注册时,只能使用OP_ACCEPT,否则抛出异常。

  • 案例演示:监听一个通道

    public class Test1 {
        public static void main(String[] args) throws Exception{
            
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            // 绑定端口号
            ssc1.bind(new InetSocketAddress(7777));
    
            // 设置非阻塞
            ssc1.configureBlocking(false);
    
            // 获取Selector选择器对象
            Selector selector = Selector.open();
    
            // 把服务器通道的accept()交给选择器来处理
            // 注册Channel到Selector选择器上
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
        }
    }
    
  • 示例:服务器创建3个通道,同时监听3个端口,并将3个通道注册到一个选择器中

public class Test2 {
    public static void main(String[] args) throws Exception{
        /*
            把多个Channel注册到一个选择器上
         */
        // 获取ServerSocketChannel服务器通道对象
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        // 绑定端口号
        ssc1.bind(new InetSocketAddress(7777));

        // 获取ServerSocketChannel服务器通道对象
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        // 绑定端口号
        ssc2.bind(new InetSocketAddress(8888));

        // 获取ServerSocketChannel服务器通道对象
        ServerSocketChannel ssc3 = ServerSocketChannel.open();
        // 绑定端口号
        ssc3.bind(new InetSocketAddress(9999));

        // 设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 获取Selector选择器对象
        Selector selector = Selector.open();

        // 把服务器通道的accept()交给选择器来处理
        // 注册Channel到Selector选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector,SelectionKey.OP_ACCEPT);
        ssc3.register(selector,SelectionKey.OP_ACCEPT);
    }
}

接下来,就可以通过选择器selector操作三个通道了。

Selector的常用方法

Selector的select()方法:

  • 作用: 服务器等待客户端连接的方法
  • 阻塞问题:
    • 在连接到第一个客户端之前,会一直阻塞
    • 当连接到客户端后,如果客户端没有被处理,该方法会计入不阻塞状态
    • 当连接到客户端后,如果客户端有被处理,该方法又会进入阻塞状态
    public class Server1 {
        public static void main(String[] args) throws Exception {
           
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            // 绑定端口号
            ssc1.bind(new InetSocketAddress(7777));
    
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc2 = ServerSocketChannel.open();
            // 绑定端口号
            ssc2.bind(new InetSocketAddress(8888));
      
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc3 = ServerSocketChannel.open();
            // 绑定端口号
            ssc3.bind(new InetSocketAddress(9999));
      
            // 设置非阻塞
            ssc1.configureBlocking(false);
            ssc2.configureBlocking(false);
            ssc3.configureBlocking(false);
    
            // 获取Selector选择器对象
            Selector selector = Selector.open();
    
            // 把服务器通道的accept()交给选择器来处理
            // 注册Channel到Selector选择器上
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
            ssc2.register(selector, SelectionKey.OP_ACCEPT);
            ssc3.register(selector, SelectionKey.OP_ACCEPT);
    
            // 死循环一直接受客户端的连接请求
            while (true) {
                System.out.println(1);
                // 服务器等待客户端的连接
                selector.select();// 阻塞
                System.out.println(2);
    
                // 处理客户端请求的代码--->暂时看不懂,先放着
                Set<SelectionKey> keySet = selector.selectedKeys();// 存储所有被连接的服务器Channel对象
                for (SelectionKey key : keySet) {
                    ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                    SocketChannel sc = ssc.accept();
                    System.out.println("...开始处理,接受数据,代码省略...");
                    //...
                }
            }
        }
    }
    
    

Selector的selectedKeys()方法

  • 获取已连接的所有通道集合

    public class Server2 {
        public static void main(String[] args) throws Exception {
            /*
                - Selector的selectedKeys()方法
                    作用:  获取所有被连接的服务器Channel对象的Set集合
                           该Set集合中的元素类型是SelectionKey,该SelectionKey类其实就是对Channel的一个封装
                    如何获取被连接的服务器Channel对象:
                        遍历所有被连接的服务器Channel对象的Set集合
                        获取该集合中的SelectionKey对象
                        根据SelectionKey对象调用channel方法,获得服务器Channel对象
                - Selector的keys()方法
             */
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            // 绑定端口号
            ssc1.bind(new InetSocketAddress(7777));
    
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc2 = ServerSocketChannel.open();
            // 绑定端口号
            ssc2.bind(new InetSocketAddress(8888));
     
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc3 = ServerSocketChannel.open();
            // 绑定端口号
            ssc3.bind(new InetSocketAddress(9999));
      
            // 设置非阻塞
            ssc1.configureBlocking(false);
            ssc2.configureBlocking(false);
            ssc3.configureBlocking(false);
    
            // 获取Selector选择器对象
            Selector selector = Selector.open();
    
            // 把服务器通道的accept()交给选择器来处理
            // 注册Channel到Selector选择器上
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
            ssc2.register(selector, SelectionKey.OP_ACCEPT);
            ssc3.register(selector, SelectionKey.OP_ACCEPT);
    
            // 获取所有被连接的服务器Channel对象的Set集合
            // 该Set集合中的元素类型是SelectionKey,该SelectionKey类其实就是对Channel的一个封装
            Set<SelectionKey> keySet = selector.selectedKeys();
            System.out.println("被连接的服务器对象有多少个:"+keySet.size());// 0
    
            // 死循环一直接受客户端的连接请求
            while (true) {
                System.out.println(1);
                // 服务器等待客户端的连接
                selector.select();// 阻塞
                System.out.println(2);
                System.out.println("被连接的服务器对象个数:"+keySet.size());// 有多少个客户端连接服务器成功,就打印几
    
                // 处理客户端请求的代码--->暂时看不懂,先放着
                // 获取所有被连接的服务器Channel对象的集合
                /*Set<SelectionKey> keySet = selector.selectedKeys();
                // 遍历所有被连接的服务器Channel对象,拿到每一个SelectionKey
                for (SelectionKey key : keySet) {
                    // 根据SelectionKey获取服务器Channel对象
                    ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                    // 获得客户端Channel对象
                    SocketChannel sc = ssc.accept();
                    // 处理
                    System.out.println("...开始处理,接受数据,代码省略...");
                    //...
                }*/
            } 
        }
    }
    
    

Selector的keys()方法

  • 获取已注册的所有通道集合

    public class Server3 {
        public static void main(String[] args) throws Exception {
            /*
                - Selector的keys()方法
                    获取所有被注册的服务器Channel对象的Set集合
                    该Set集合中的元素类型是SelectionKey,该SelectionKey类其实就是对Channel的一个封装
             */
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            // 绑定端口号
            ssc1.bind(new InetSocketAddress(7777));
    
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc2 = ServerSocketChannel.open();
            // 绑定端口号
            ssc2.bind(new InetSocketAddress(8888));  
    
            // 获取ServerSocketChannel服务器通道对象
            ServerSocketChannel ssc3 = ServerSocketChannel.open();
            // 绑定端口号
            ssc3.bind(new InetSocketAddress(9999));  
    
            // 设置非阻塞
            ssc1.configureBlocking(false);
            ssc2.configureBlocking(false);
            ssc3.configureBlocking(false);
    
            // 获取Selector选择器对象
            Selector selector = Selector.open();
    
            // 把服务器通道的accept()交给选择器来处理
            // 注册Channel到Selector选择器上
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
            ssc2.register(selector, SelectionKey.OP_ACCEPT);
            ssc3.register(selector, SelectionKey.OP_ACCEPT);
    
            // 获取所有被连接的服务器Channel对象的Set集合
            // 该Set集合中的元素类型是SelectionKey,该SelectionKey类其实就是对Channel的一个封装
            Set<SelectionKey> keySet = selector.selectedKeys();
            System.out.println("被连接的服务器对象有多少个:"+keySet.size());// 0
    
            // 获取所有被注册的服务器Channel对象的Set集合
            // 该Set集合中的元素类型是SelectionKey,该SelectionKey类其实就是对Channel的一个封装
            Set<SelectionKey> keys = selector.keys();
            System.out.println("被注册的服务器对象有多少个:"+keys.size()); // 3  
    
            // 死循环一直接受客户端的连接请求
            while (true) {
                System.out.println(1);
                // 服务器等待客户端的连接
                selector.select();// 阻塞
                System.out.println(2);
                System.out.println("被连接的服务器对象个数:"+keySet.size());// 有多少个客户端连接服务器成功,就打印几
                System.out.println("被注册的服务器对象个数:"+keys.size());// 选择器上注册了多少个服务器Channel,就打印几
            }  
        }
    }
    

Selector多路复用

需求

  • 使用Selector进行多路复用,监听3个服务器端口

分析

  • 创建3个服务器通道,设置成非阻塞
  • 获取Selector选择器
  • 把Selector注册到三个服务器通道上
  • 循环去等待客户端连接
  • 遍历所有被连接的服务器通道集合
  • 处理客户端请求

实现

  • 案例:

    public class Server1 {
        public static void main(String[] args) throws Exception {
            /*
                需求: 使用Selector进行多路复用,监听3个服务器端口
                分析:
                    1.创建3个服务器Channel对象,并绑定端口号
                    2.把3个服务器Channel对象设置成非阻塞
                    3.获得Selector选择器
                    4.把3个个服务器Channel对象对象注册到同一个Selector选择器上,指定监听事件
                    5.死循环去等待客户端的连接
                    6.获取所有被连接的服务器Channel对象的Set集合
                    7.循环遍历所有被连接的服务器Channel对象
                    8.处理客户端的请求
             */
            // 1.创建3个服务器Channel对象,并绑定端口号
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            ssc1.bind(new InetSocketAddress(7777));
    
            ServerSocketChannel ssc2 = ServerSocketChannel.open();
            ssc2.bind(new InetSocketAddress(8888));
    
            ServerSocketChannel ssc3 = ServerSocketChannel.open();
            ssc3.bind(new InetSocketAddress(9999));
    
            // 2.把3个服务器Channel对象设置成非阻塞
            ssc1.configureBlocking(false);
            ssc2.configureBlocking(false);
            ssc3.configureBlocking(false);
    
            // 3.获得Selector选择器
            Selector selector = Selector.open();
    
            // 4.把3个个服务器Channel对象对象注册到同一个Selector选择器上,指定监听事件
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
            ssc2.register(selector, SelectionKey.OP_ACCEPT);
            ssc3.register(selector, SelectionKey.OP_ACCEPT);
    
            // 5.死循环去等待客户端的连接
            while (true) {
                // 服务器等待客户端连接
                System.out.println(1);
                selector.select();
    
                // 6.获取所有被连接的服务器Channel对象的Set集合
                Set<SelectionKey> keySet = selector.selectedKeys(); // 2
    
                // 7.循环遍历所有被连接的服务器Channel对象,获取每一个被连接的服务器Channel对象
                for (SelectionKey key : keySet) {// 遍历出7777端口  8888端口
                    // 8.由于SelectionKey是对Channel的封装,所以我们得根据key获取被连接的服务器Channel对象
                    ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                    // 9.处理客户端的请求
                    // 9.1 获取连接的客户端对象
                    SocketChannel sc = ssc.accept();
                    // 9.2 创建ByteBuffer缓冲数组
                    ByteBuffer b = ByteBuffer.allocate(1024);
                    // 9.3 读取数据
                    int len = sc.read(b);// 把读取到的字节数据存储到b缓冲数组中,返回读取到的字节个数
                    // 9.4 打印输出
                    System.out.println(new String(b.array(), 0, len));
                    // 10. 释放资源
                    sc.close();
                }
            }         
        }
    }
    
    
  • 问题: Selector把所有被连接的服务器对象放在了一个Set集合中,但是使用完后并没有删除,导致在遍历集合时,遍历到已经没用的对象,出现了异常

  • 解决办法: 使用完了,应该从集合中删除,由于遍历的同时不能删除,所以使用迭代器进行遍历

  • 代码如下:

    public class Server2 {
        public static void main(String[] args) throws Exception {
           
            // 1.创建3个服务器Channel对象,并绑定端口号
            ServerSocketChannel ssc1 = ServerSocketChannel.open();
            ssc1.bind(new InetSocketAddress(7777));
    
            ServerSocketChannel ssc2 = ServerSocketChannel.open();
            ssc2.bind(new InetSocketAddress(8888));
    
            ServerSocketChannel ssc3 = ServerSocketChannel.open();
            ssc3.bind(new InetSocketAddress(9999));
    
            // 2.把3个服务器Channel对象设置成非阻塞
            ssc1.configureBlocking(false);
            ssc2.configureBlocking(false);
            ssc3.configureBlocking(false);
    
            // 3.获得Selector选择器
            Selector selector = Selector.open();
    
            // 4.把3个个服务器Channel对象对象注册到同一个Selector选择器上,指定监听事件
            ssc1.register(selector, SelectionKey.OP_ACCEPT);
            ssc2.register(selector, SelectionKey.OP_ACCEPT);
            ssc3.register(selector, SelectionKey.OP_ACCEPT);
    
            // 5.死循环去等待客户端的连接
            while (true) {
                // 服务器等待客户端连接
                System.out.println(1);
                selector.select();
    
                // 6.获取所有被连接的服务器Channel对象的Set集合
                Set<SelectionKey> keySet = selector.selectedKeys();
    
                // 7.循环遍历所有被连接的服务器Channel对象,获取每一个被连接的服务器Channel对象
                Iterator<SelectionKey> it = keySet.iterator();
                // 迭代器的快捷键: itit
                while (it.hasNext()){
    
                    // 遍历出来的SelectionKey
                    SelectionKey key = it.next();
    
                    // 8.由于SelectionKey是对Channel的封装,所以我们得根据key获取被连接的服务器Channel对象
                    ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
                    // 9.处理客户端的请求
                    // 9.1 获取连接的客户端对象
                    SocketChannel sc = ssc.accept();
                    // 9.2 创建ByteBuffer缓冲数组
                    ByteBuffer b = ByteBuffer.allocate(1024);
                    // 9.3 读取数据
                    int len = sc.read(b);// 把读取到的字节数据存储到b缓冲数组中,返回读取到的字节个数
                    // 9.4 打印输出
                    System.out.println(new String(b.array(), 0, len));
                    // 10. 释放资源
                    sc.close();
    
                    // 用完了就得删除
                    it.remove();
                }
            }
        }
    }
    

NIO2-AIO(异步、非阻塞)

AIO概述

同步,异步,阻塞,非阻塞概念回顾

- 同步:调用方法之后,必须要得到一个返回值。

- 异步:调用方法之后,没有返回值,但是会有回调函数。回调函数指的是满足条件之后会自动执行的方法

- 阻塞:如果没有达到方法的目的,就一直停在这里【等待】。  

- 非阻塞:不管有没有达到目的,都直接【往下执行】。  

在这里插入图片描述

AIO相关类和方法介绍

AIO是异步IO的缩写,虽然NIO在网络操作中,提供了非阻塞的方法,但是NIO的IO行为还是同步的。对于NIO来说,我们的业务线程是在IO操作准备好时,得到通知,接着就由这个线程自行进行IO操作,IO操作本身是同步的。

但是对AIO来说,则更加进了一步,它不是在IO准备好时再通知线程,而是在IO操作已经完成后,再给线程发出通知。因此AIO是不会阻塞的,此时我们的业务逻辑将变成一个回调函数,等待IO操作完成后,由系统自动触发。

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2---->AIO,主要在Java.nio.channels包下增加了下面四个异步通道:

  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousFileChannel
  • AsynchronousDatagramChannel

在AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,这个类提供了一个open()静态工厂,一个bind()方法用于绑定服务端IP地址(还有端口号),另外还提供了accept()用于接收用户连接请求。在客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法外,还提供了read和write方法。

在AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO中的事件处理类是CompletionHandler<V,A>,这个接口定义了如下两个方法,分别在异步操作成功和失败时被回调。

void completed(V result, A attachment);

void failed(Throwable exc, A attachment);

AIO 同步连接同步读(没有意义)

需求

  • AIO同步写法,读取客户端写过来的数据

分析

  • 获取AsynchronousServerSocketChannel对象,绑定端口
  • 同步接收客户端请求
  • 读取数据

实现

  public static void main(String[] args) throws Exception {
        //创建对象
        AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();

        //绑定端口
        assc.bind(new InetSocketAddress(8888));

        //获取连接
        //Future里面放的就是方法的结果
        //********同步********
        System.out.println("准备连接客户端");
        Future<AsynchronousSocketChannel> future = assc.accept();
        //Future方法需要调用get()方法获取真正的返回值
        AsynchronousSocketChannel sc = future.get();
        System.out.println("连接上了客户端");
        //读取客户端发来的数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //读取
        //以前返回的是读取到的个数,真正的个数就在Future里面放着
        //********同步********
        System.out.println("准备读取数据");
        Future<Integer> future2 = sc.read(buffer);
        //获取真正的返回值
        Integer len = future2.get();
        System.out.println("读取到了数据");
        //打印
        System.out.println(new String(buffer.array(),0,len));
    }

AIO 异步非阻塞连接

需求

  • AIO异步非阻塞的连接方法

分析

  • 获取AsynchronousServerSocketChannel对象,绑定端口
  • 异步接收客户端请求
    • void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler)
    • 第一个参数: 附件,没啥用,传入null即可
    • 第二个参数: CompletionHandler抽象类 ,AIO中的事件处理类
      • void completed(V result, A attachment);异步连接成功,就会自动调用这个方法
      • void failed(Throwable exc, A attachment);异步连接失败,就会自动调用这个方法

实现

  • 服务器端:
public static void main(String[] args) throws IOException {
        //服务器异步的连接方法
        //创建对象
        AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
        //绑定端口
        assc.bind(new InetSocketAddress(8000));
        //【异步非阻塞】方式!!!!!连接客户端
        System.out.println("11111111111");
        assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            //回调函数,当成功连接了客户端之后,会自动回来调用这个方法
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                //completed是完成,如果连接成功会自动调用这个方法
                System.out.println("completed");
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                //failed是失败,如果连接失败会自动调用这个方法
            }
        });
        System.out.println("22222222222");

        //写一个死循环让程序别结束(因为程序结束了无法演示效果)
        while(true){
        }
    }

AIO 异步非阻塞连接和异步读

需求

  • 实现异步连接,异步读

分析

  • 获取AsynchronousServerSocketChannel对象,绑定端口
  • 异步接收客户端请求
  • 在CompletionHandler的completed方法中异步读数据

实现

  • 服务器端代码:
//异步非阻塞连接和读取
    public static void main(String[] args) throws IOException {
        //创建对象
        AsynchronousServerSocketChannel assc  = AsynchronousServerSocketChannel.open();
        //绑定端口
        assc.bind(new InetSocketAddress(8000));
        //异步非阻塞连接!!!!
        //第一个参数是一个附件
        System.out.println(1);
        assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel s, Object attachment) {
                //如果连接客户端成功,应该获取客户端发来的数据
                //completed()的第一个参数表示的是Socket对象.
                System.out.println(5);
                //创建数组
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //异步非阻塞读!!!!!
                System.out.println(3);
                s.read(buffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer len, Object attachment) {
                        //读取成功会自动调用这个方法
                        //completed()方法的第一个参数是read()读取到的实际个数
                        //打印数据
                        System.out.println(6);
                        System.out.println(new String(buffer.array(),0,len));
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {

                    }
                });
                System.out.println(4);
            }

            @Override
            public void failed(Throwable exc, Object attachment) {

            }
        });

        System.out.println(2);

        //让程序别结束写一个死循环
        while(true){

        }
    }

AIO 异步非阻塞客户端请求连接(没有意思)

需求

  • 完成异步非阻塞客户端请求连接

分析

  • 创建客户端对象
  • 异步请求连接服务器

实现

public class Demo客户端AIO {
    public static void main(String[] args) throws IOException {
        //客户端也有AIO对象
        AsynchronousSocketChannel asc = AsynchronousSocketChannel.open();
        System.out.println(1);
        //指定要连接的服务器的ip和端口
        asc.connect(new InetSocketAddress("123.23.44.3",8000), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                //成功就执行这个方法
                System.out.println(3);
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                //失败就执行这个方法
                System.out.println(4);

            }
        });
        System.out.println(2);

        //写一个循环让main方法别结束
        while(true){

        }
    }
}
如果连接成功:
	1
	2
	3
	
如果连接失败:
	1
	2
	4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值