Java=NIO,AIO详解

1.NIO的Buffer,Channel,Selector(选择器,多路复用器)
2.AIO是异步非阻塞IO

一。Selector 选择器

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

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

服务器端的非多路复用的效果:

如果不使用 多路复用,服务器端需要开很多线程处理每个端口的请求,如果在高并发环境下,造成系统性能下降。

服务器端的多路复用效果:

使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少cpu切换时间,在高并发,高频段业务环境下有非常重要的优势。

选择器Selector

Selector称为 选择器,也称为 多路复用器,他可以注册到很多个 Channel上,监听各个Channel上发生的事件,并且能够根据事件情况决定Channel读写,这样,通过一个线程管理多个Channel,就可以处理大量网络连接了

有了Selector,我们可以利用一个线程来处理所有的Channel,线程之前的切换对操作系统来说代价太高,并且每一个线程也会占用一定的系统资源,所以,对系统来说使用的线程越少越好。

创建一个Selector:

创建Selector的方式
    Selector selector = Selector.open();

注册Channel到Selector

为了能让Channel 和 Selector 配合使用,我们需要把Channel注册到 Selector上,通过调用channel.register()方法来实现注册

channel . configureBlocking ( false );
SelectionKey key = channel . register ( selector , SelectionKey . OP_READ );
注意
注册的 Channel 必须设置成异步模式才可以 , 否则异步 IO 就无法工作,这就意味着我们不能把一
FileChannel 注册到 Selector ,因为 FileChannel 没有异步模式,但是网络编程中的 SocketChannel
可以的。
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 SelectorDemo01 {
    public static void main(String[] args) throws IOException {
        //1.多路(多个服务器监听多个端口)
        ServerSocketChannel server1 = ServerSocketChannel.open();
        server1.configureBlocking(false); //一定要改为非阻塞
        server1.bind(new InetSocketAddress(7777));

        ServerSocketChannel server2 = ServerSocketChannel.open();
        server2.configureBlocking(false); //一定要改为非阻塞
        server2.bind(new InetSocketAddress(8888));

        ServerSocketChannel server3 = ServerSocketChannel.open();
        server3.configureBlocking(false); //一定要改为非阻塞
        server3.bind(new InetSocketAddress(9999));

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

        //3.将多个server注册到同一个Selector上
        se,那么rver1.register(selector, SelectionKey.OP_ACCEPT);
        server2.register(selector, SelectionKey.OP_ACCEPT);
        server3.register(selector, SelectionKey.OP_ACCEPT);
    }
}

Selector中的常用方法

Selector keys() 方法
此方法返回一个 Set 集合,表示:已注册通道的集合。每个已注册通道封装为一个
SelectionKey 对象。
Selector selectedKeys() 方法
此方法返回一个 Set 集合,表示:当前已连接的通道的集合。每个已连接通道统一封装为一个
SelectionKey 对象。
Selector select() 方法
此方法会阻塞,直到有至少 1 个客户端连接。
此方法会返回一个 int 值,表示有几个客户端连接了服务器。

示例:


public class SelectorDemo02 {
    public static void main(String[] args) throws IOException {
        //1.多路(多个服务器监听多个端口)
        ServerSocketChannel server1 = ServerSocketChannel.open();
        server1.configureBlocking(false); //一定要改为非阻塞
        server1.bind(new InetSocketAddress(7777));

        ServerSocketChannel server2 = ServerSocketChannel.open();
        server2.configureBlocking(false); //一定要改为非阻塞
        server2.bind(new InetSocketAddress(8888));

        ServerSocketChannel server3 = ServerSocketChannel.open();
        server3.configureBlocking(false); //一定要改为非阻塞
        server3.bind(new InetSocketAddress(9999));

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

        //3.将多个server注册到同一个Selector上
        server1.register(selector, SelectionKey.OP_ACCEPT);
        server2.register(selector, SelectionKey.OP_ACCEPT);
        server3.register(selector, SelectionKey.OP_ACCEPT);

        //4.接收客户端连接
        Set<SelectionKey> keys = selector.keys();//获取已注册通道的集合
        System.out.println("注册通道数量:" + keys.size());
        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());
    }
}

public class SocketChannelDemo {
    public static void main(String[] args) {

        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("7777客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 7777));
                System.out.println("7777客户端连接成功....");
            } catch (IOException e) {
                System.out.println("7777异常重连");
            }
        }).start();


        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("8888客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 8888));
                System.out.println("8888客户端连接成功....");
            } catch (IOException e) {
                System.out.println("8888异常重连");
            }
        }).start();


        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("9999客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 9999));
                System.out.println("9999客户端连接成功....");
            } catch (IOException e) {
                System.out.println("9999异常重连");
            }
        }).start();
    }
}

Selector实现多路连接:

public class SelectorDemo02 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.多路(多个服务器监听多个端口)
        ServerSocketChannel server1 = ServerSocketChannel.open();
        server1.configureBlocking(false); //一定要改为非阻塞
        server1.bind(new InetSocketAddress(7777));

        ServerSocketChannel server2 = ServerSocketChannel.open();
        server2.configureBlocking(false); //一定要改为非阻塞
        server2.bind(new InetSocketAddress(8888));

        ServerSocketChannel server3 = ServerSocketChannel.open();
        server3.configureBlocking(false); //一定要改为非阻塞
        server3.bind(new InetSocketAddress(9999));

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

        //3.将多个server注册到同一个Selector上
        server1.register(selector, SelectionKey.OP_ACCEPT);
        server2.register(selector, SelectionKey.OP_ACCEPT);
        server3.register(selector, SelectionKey.OP_ACCEPT);

        //4.接收客户端连接
        Set<SelectionKey> keys = selector.keys();//获取已注册通道的集合
        System.out.println("注册通道数量:" + keys.size());
        Set<SelectionKey> selectionKeys = selector.selectedKeys();//获取已连接通 道的集合
        System.out.println("已连接通道数量:" + selectionKeys.size());
        System.out.println("----------------------------------------------");
        while (true) {
            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());

            System.out.println("============处理被连接的服务器通道============");
            //遍历已连接通道的集合
            Iterator<SelectionKey> it = selectionKeys1.iterator();
            while (it.hasNext()) {
                //获取当前连接通道的
                SelectionKey key = it.next();
                //从SelectionKey中获取通道对象
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                //看一下此通道是监听哪个端口的
                System.out.println("监听端口:" + channel.getLocalAddress());
                //取出连接到该服务器的客户端通道
                SocketChannel accept = channel.accept();
                System.out.println(accept);
                System.out.println("写与该客户端进行交互的代码....");
                //从连接的通道中把已经处理过的服务器通道移出
                it.remove();
            }
            System.out.println("休息5秒......");
            Thread.sleep(5000);
            System.out.println();//打印一个空行
        }
    }
}

public class SocketChannelDemo {
    public static void main(String[] args) {

        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("7777客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 7777));
                System.out.println("7777客户端连接成功....");
            } catch (IOException e) {
                System.out.println("7777异常重连");
            }
        }).start();


        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("8888客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 8888));
                System.out.println("8888客户端连接成功....");
            } catch (IOException e) {
                System.out.println("8888异常重连");
            }
        }).start();


        new Thread(() -> {
            try (SocketChannel socket = SocketChannel.open()) {
                System.out.println("9999客户端连接服务器......");
                socket.connect(new InetSocketAddress("127.0.0.1", 9999));
                System.out.println("9999客户端连接成功....");
            } catch (IOException e) {
                System.out.println("9999异常重连");
            }
        }).start();
    }
}

Selector多路信息接收:

public class SelectorDemo02 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.多路(多个服务器监听多个端口)
        ServerSocketChannel server1 = ServerSocketChannel.open();
        server1.configureBlocking(false); //一定要改为非阻塞
        server1.bind(new InetSocketAddress(7777));

        ServerSocketChannel server2 = ServerSocketChannel.open();
        server2.configureBlocking(false); //一定要改为非阻塞
        server2.bind(new InetSocketAddress(8888));

        ServerSocketChannel server3 = ServerSocketChannel.open();
        server3.configureBlocking(false); //一定要改为非阻塞
        server3.bind(new InetSocketAddress(9999));

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

        //3.将多个server注册到同一个Selector上
        server1.register(selector, SelectionKey.OP_ACCEPT);
        server2.register(selector, SelectionKey.OP_ACCEPT);
        server3.register(selector, SelectionKey.OP_ACCEPT);

        //4.接收客户端连接
        Set<SelectionKey> keys = selector.keys();//获取已注册通道的集合
        System.out.println("注册通道数量:" + keys.size());
        Set<SelectionKey> selectionKeys = selector.selectedKeys();//获取已连接通 道的集合
        System.out.println("已连接通道数量:" + selectionKeys.size());
        System.out.println("----------------------------------------------");
        while (true) {
            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());

            System.out.println("============处理被连接的服务器通道============");
            //遍历已连接通道的集合
            Iterator<SelectionKey> it = selectionKeys1.iterator();
            while (it.hasNext()) {
                //获取当前连接通道的
                SelectionKey key = it.next();
                //从SelectionKey中获取通道对象
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                //看一下此通道是监听哪个端口的
                System.out.println("监听端口:" + channel.getLocalAddress());
                //取出连接到该服务器的客户端通道
                SocketChannel accept = channel.accept();
                System.out.println("写与该客户端进行交互的代码....");
                //接收客户端发送过来的信息
                ByteBuffer inBuf = ByteBuffer.allocate(1000);
                accept.read(inBuf);
                inBuf.flip();
                String msg = new String(inBuf.array(), 0, inBuf.limit());
                System.out.println("【服务器】接收到通道【" + channel.getLocalAddress() + "】的信息:" + msg);


                //从连接的通道中把已经处理过的服务器通道移出
                it.remove();
            }
            System.out.println("休息5秒......");
            Thread.sleep(5000);
            System.out.println();//打印一个空行
        }
    }
}
关于 SelectionKey
- 当一个 " 通道 " 注册到选择器 Selector 后,选择器 Selector 内部就创建一个 SelectionKey 对象,里面封
装了这个通道和这个选择器的映射关系。
- 通过 SelectionKey channel() 方法,可以获取它内部的通道对象。
解决 select() 不阻塞,导致服务器端死循环的问题
原因:在将 " 通道 " 注册到 " 选择器 Selector" 时,我们指定了关注的事件 SelectionKey.OP_ACCEPT
而我们获取到管道对象后,并没有处理这个事件,所以导致 select() 方法一直循环。
解决:处理 SelectionKey.OP_ACCEPT 事件
 

 

二。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,主要在Java.nio.channels包下增加了下面四个异步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
AIO socket编程中,服务端通道是AsynchronousServerSocketChannel,这个类提供了一个open()
静态工厂,一个bind()方法用于绑定服务端IP地址(还有端口号),另外还提供了accept()用于接收用户
连接请求。在客户端使用的通道是AsynchronousSocketChannel,这个通道处理提供open静态工厂方法
外,还提供了readwrite方法。
AIO编程中,发出一个事件(accept read write等)之后要指定事件处理类(回调函数),AIO中的
事件处理类是CompletionHandler<V,A>,这个接口定义了如下两个方法,分别在异步操作成功和失败
时被回调。
void completed(V result, A attachment);
void failed(Throwable exc, A attachment);
 
ASynchronized 异步非阻塞的IO
AIO的分类:
    异步的文件通道  AsynchronousFileChannel
    异步的客户端通道: AsynchronousSocketChannel
    异步的服务器通道: AsynchronousServerSocketChannel
    异步的UDP协议通道: AsynchronousDatagramChannel
AIO的异步:
    表现两个方面:
        a.连接时,可以使用异步(调用连接的方法时,非阻塞,连接成功之后会以方法回调的机制通知我们)
        b.读写数据时,可以使用异步(调用read方法时,非阻塞,等数据接收到之后以方法调用的机制通知我们)
 
 
AIO异步IO非阻塞连接建立
 
异步非阻塞客户端通道
 
/**
 * AIO下的 异步客户端通道
 */
public class AIOSocketChannelDemo01 {
    public static void main(String[] args) throws IOException {
        //1.创建 异步的客户端通道
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //2.去连接服务器,采用异步非阻塞方法
        //connect(服务器的IP和端口号,附件(null),接口);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                System.out.println("连接服务器成功...");
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("连接服务器失败...");
            }
        });

        System.out.println("程序继续执行....");

        while (true) {

        }
    }
}

异步非阻塞服务器通道

/**
 * AIO下的 异步服务器端通道
 */
public class AIOServerSocketChannelDemo01 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建一个异步的服务器通道
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        //2.绑定本地某个端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.接收异步客户端,采用异步非阻塞方式
        //accpet(附件(nulll),接口);
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                System.out.println("有客户端连接....");
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("客户端连接失败...");
            }
        });

        System.out.println("程序继续执行..");

        while (true) {

        }

    }
}

2.AIO同步读写数据:

/**
 * AIO下的 异步客户端通道
 */
public class AIOSocketChannelDemo01 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建 异步的客户端通道
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //2.去连接服务器,采用异步非阻塞方法
        //connect(服务器的IP和端口号,附件(null),接口);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                System.out.println("连接服务器成功...");
                //客户端给服务器发送数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                buffer.put("你好我是AIO客户端...".getBytes());
                //切换读写模式
                buffer.flip();
                socketChannel.write(buffer);
                //释放资源
                try {
                    socketChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("连接服务器失败...");
            }
        });

        System.out.println("程序继续执行....");
    }
}
/**
 * AIO下的 异步服务器端通道
 */
public class AIOServerSocketChannelDemo01 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建一个异步的服务器通道
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        //2.绑定本地某个端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.接收异步客户端,采用异步非阻塞方式
        //accpet(附件(nulll),接口);
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                System.out.println("有客户端连接....");
                //去读客户端发送来的数据
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                Future<Integer> future = socketChannel.read(buffer);
//            
                try {
                    byte[] array = buffer.array();
                    System.out.println(Arrays.toString(array));

                    Integer len = future.get();
                    System.out.println(len);
					//当调用future的get方法时,数据才写入到buffer中
                    //所以我们不能在调用get方法之前,调用flip,否则数据将无法写入
                    buffer.flip();
                    System.out.println(new String(array,0,len));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("客户端连接失败...");
            }
        });

        System.out.println("程序继续执行..");
    }
}

3.AIO异步读写数据

/**
 * AIO下的 异步客户端通道
 */
public class AIOSocketChannelDemo01 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建 异步的客户端通道
        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        //2.去连接服务器,采用异步非阻塞方法
        //connect(服务器的connectIP和端口号,附件(null),接口);
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                System.out.println("连接服务器成功...");
                //给服务器发送数据
                ByteBuffer buffer = ByteBuffer.allocate(1000);
                buffer.put("你好我是AIO客户端..".getBytes());
                buffer.flip();
                //异步的write(缓冲区,超时时间,时间单位,附件(null),回调接口);
                socketChannel.write(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("数据成功发送...");
                        //释放资源
                        try {
                            socketChannel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("数据发送失败...");
                    }
                });


            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("连接服务器失败...");
            }
        });

        System.out.println("程序继续执行....");
        Thread.sleep(5000);
    }
}


/**
 * AIO下的 异步服务器端通道
 */
public class AIOServerSocketChannelDemo01 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建一个异步的服务器通道
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        //2.绑定本地某个端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        //3.接收异步客户端,采用异步非阻塞方式
        //accpet(附件(nulll),接口);
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                System.out.println("有客户端连接....");
                //从客户端中读取数据
                //异步的read(字节缓冲区,超时时间,时间单位,附件(null),回调接口)
                ByteBuffer buffer = ByteBuffer.allocate(1000);
                socketChannel.read(buffer, 10, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("数据读取完毕..");
                        System.out.println("接收到数据的长度:"+result);
                        System.out.println("接收到的数据是:" + new String(buffer.array(), 0, result));
                        //释放资源
                        try {
                            socketChannel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("读取数据失败...");
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("客户端连接失败...");
            }
        });

        System.out.println("程序继续执行..");
        while (true) {
            Thread.sleep(1000);
        }

    }
}
other===================================================
 
 
one---
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class oneClient {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try (SocketChannel socketChannel = SocketChannel.open()){
                    System.out.println("7777 客户端连接服务器。。。。。。");
                    socketChannel.connect(new InetSocketAddress("127.0.0.1",7777));
                    System.out.println("7777 客户端连接成功。。。。。。");
                    ByteBuffer allocate = ByteBuffer.allocate(1024);
                  allocate.put("我是 7777 客户端".getBytes());
                  allocate.flip();
                  socketChannel.write(allocate);
                }catch (IOException e){
                    e.printStackTrace();
                    System.out.println("7777连接异常");
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try (SocketChannel socketChannel = SocketChannel.open()){
                    System.out.println("8888 客户端连接服务器。。。。。。");
                    socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
                    System.out.println("8888 客户端连接成功。。。。。。");
                    ByteBuffer allocate = ByteBuffer.allocate(1024);
                    allocate.put("我是 8888 客户端".getBytes());
                    allocate.flip();
                    socketChannel.write(allocate);
                }catch (IOException e){
                    e.printStackTrace();
                    System.out.println("8888 连接异常");
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try (SocketChannel socketChannel = SocketChannel.open()){
                    System.out.println("9999 客户端连接服务器。。。。。。");
                    socketChannel.connect(new InetSocketAddress("127.0.0.1",9999));
                    System.out.println("9999 客户端连接成功。。。。。。");
                    ByteBuffer allocate = ByteBuffer.allocate(1024);
                    allocate.put("我是 9999 客户端".getBytes());
                    allocate.flip();
                    socketChannel.write(allocate);
                }catch (IOException e){
                    e.printStackTrace();
                    System.out.println("9999 连接异常");
                }
            }
        }).start();



    }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class oneService {
    public static void main(String[] args) throws Exception{

        //创建三个通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(7777));

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

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

        //创建一个Selector
        Selector selector = Selector.open();
        //注册三个通道
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        serverSocketChannel01.register(selector,SelectionKey.OP_ACCEPT);
        serverSocketChannel02.register(selector,SelectionKey.OP_ACCEPT);

        //获取已经注册通道的集合
        Set<SelectionKey> keys = selector.keys();
        //
        System.out.println("=注册的通道数量="+keys.size());
        //获取已连接通道集合
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        System.out.println("=已连接的通道数量="+selectionKeys.size());
        System.out.println("========================================================");

        while (true){
            System.out.println("【服务器】等待连接。。。");
            int select = selector.select();
            System.out.println("服务器连接的数量:"+select);
            //获取已连接的通道对象
            Set<SelectionKey> selectionKeys1 = selector.selectedKeys();
            System.out.println("集合大小:"+selectionKeys1.size());

            Iterator<SelectionKey> iterator = selectionKeys1.iterator();
            while (iterator.hasNext()){
                SelectionKey next = iterator.next();
                ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                System.out.println("监听端口:"+channel.getLocalAddress());
                SocketChannel accept = channel.accept();

                ByteBuffer allocate = ByteBuffer.allocate(1024);
                accept.read(allocate);
                allocate.flip();
                String string = new String(allocate.array(), 0, allocate.limit());
                System.out.println("【服务器】接收到通道【"+channel.getLocalAddress()+"】的信息:"+string);

                iterator.remove();

            }
            System.out.println("休息1秒");
            Thread.sleep(1000);
        }

    }

}

two:AIO同步读写

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AIOClient {
    public static void main(String[] args) throws  Exception{

        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                //连接成功的回调
                System.out.println("客户端连接成功");
                socketChannel.write(ByteBuffer.wrap("我来自客户端8888端口的数据".getBytes()));
                try {
                    socketChannel.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("客户端连接失败");
            }
        });
        System.out.println("客户端继续");

    }
}


import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;

public class AIOService {

    public static void main(String[] args) throws Exception{

        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));

        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                System.out.println("服务器接收连接、、、");
                ByteBuffer allocate = ByteBuffer.allocate(1024);
                Future<Integer> read = result.read(allocate);
                try {
                    Integer len = read.get();
                    allocate.flip();
                    String string = new String(allocate.array(), 0, len);
                    System.out.println("【服务器】收到客户端的数据为:"+string);
                    result.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("IO操作失败、、、");
            }
        });

        System.out.println("服务器端继续,,,,");
        while (true){

        }

    }
}

AIO异步读写

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AIOyibuClient {
    public static void main(String[] args) throws Exception{

        AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999), null, new CompletionHandler<Void, Object>() {
            @Override
            public void completed(Void result, Object attachment) {
                ByteBuffer byteBuffer = ByteBuffer.wrap("我是来自客户端的 9999 用户".getBytes());
                socketChannel.write(byteBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        //
                        System.out.println("客户端数据--发送完毕");
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        System.out.println("客户端数据--发送失败");
                    }
                });

            }

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

            }
        });

        while (true){

        }

    }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AIOyibuSevice {
    public static void main(String[] args) throws Exception{
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9999));
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                System.out.println("服务器连接成功。。。");
               // serverSocketChannel.accept(null,this);
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                socketChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        if (result!=-1){
                            String string = new String(byteBuffer.array(), 0, result);
                            System.out.println("-服务器端收到客户端的数据为:"+string);
                        }
                        try{
                            socketChannel.close();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }

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

                    }
                });
            }

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

            }
        });

        while (true){

        }

    }
}


BIO、NIO和AIO的区别、三种IO的原理

IO

什么是IO? 它是指计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。

在 Java 编程中,直到最近一直使用 流 的方式完成 I/O。所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
BIO

Java BIO即Block I/O , 同步并阻塞的IO。

BIO就是传统的java.io包下面的代码实现。
NIO

什么是NIO? NIO 与原来的 I/O 有同样的作用和目的, 他们之间最重要的区别是数据打包和传输的方式。原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。

一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
AIO

Java AIO即Async非阻塞,是异步非阻塞的IO。
区别及联系

BIO (Blocking I/O):同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。

NIO (New I/O):同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。

AIO ( Asynchronous I/O):异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
各自适用场景

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
 

发布了117 篇原创文章 · 获赞 20 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/u010581811/article/details/105145870