NIO

一、概述

1.用于进行数据传输的

2.BIO - BlockingIO - 同步式阻塞式IO

3.NIO - NewIO - NonBlockingIO - 同步式非阻塞式IO - JDK1.4以上 - 就是专门为了应对高并发场景

4.NIO的组件:Buffer、Channel、Selector

5.NIO框架:Netty、Mina、Grizzly

6.AIO 在JDK1.8以上 - AsynchronousID :异步式非阻塞IO

   AIO的整体设计和NIO基本类似,只是多了异步改进,以及事件驱动方式做了改变,也因此成为NIO.2

  

二、BIO的缺点

1.单项流:需要创建大量的流对象,内存会被大量的占用

2.一对一连接:每一个客户端都需要有一个服务端的线程来处理;

   如果当并发量大的时候,服务器端会产生大量的线程,如果线程数量过多会导致服务器的崩溃

3.客户端连接之后即使不产生任何的操作也会占用着服务器端的线程,会导致服务器端资源的浪费 

 

三、Buffer - 缓冲区

1.在NIO中用于进行数据的存储

2.底层基于数组来进行存储的,存储的元素类型是基本类型,

   针对除了boolean以外的基本类型都提供了具体的子类,但是最常用的是ByteBuffer 

3.重要位置:capacity >= limit >= pmosition >= mark

 a. capacity:容量位。用于标记缓冲区的容量,指定之后就不可变

   b. limit:限制位。用于标记操作位所能达到的最大尺度,默认在缓冲区的最后一位

   c. position:操作位。用于指向要操作的位置,默认在第0位

   d. mark:标记位。用于标记数据的位置,一般用于进行校验。在Java中,mark默认是不启用

4.重要操作

   a. flip:反转缓冲区。将limit挪动到position上,然后将position归零,mark置位-1

 b. clear:清空缓冲区。回归缓冲区最开始状态

   c. reset:重置缓冲区。将position挪到mark上

   e. rewind:重绕缓冲区。将postion归零,将mark置位-1

 

四、Channel - 通道

1.用于进行数据的传输

2.双向通道:可以进行数据的双向传输

3.Channel默认是阻塞的,需要手动设置为非阻塞

4.通道是面向缓冲区进行操作的

5..文件:FileChannel

 UDP:DatagramChannel

 TCP:SocketChannel、ServerSocketChannel

 

五、Selector - 多路复用选择器

1.用于进行通道的选择的

2.选择器上注册的通道需要为非阻塞的

3.在选择通道的时候,是基于事件驱动机制来完成,即通道身上必须有对应的事件才会被处理

 

服务端代码:

package com.apple.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/01
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //开启服务器端的通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        //绑定端口
        ssc.bind(new InetSocketAddress(8070));
        //设置为非阻塞的
        ssc.configureBlocking(false);
        //开启选择器
        Selector selc = Selector.open();
        //将通道注册到选择器上
        ssc.register(selc, SelectionKey.OP_ACCEPT);

        while (true) {
            //选择,选择出已经注册的通道
            selc.select();
            //获取这次选择出来的通道的事件
            Set<SelectionKey> keys = selc.selectedKeys();
            Iterator<SelectionKey> it = keys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //接收
                if (key.isAcceptable()) {
                    //获取通道
                    ServerSocketChannel sscx = (ServerSocketChannel) key.channel();
                    //接收连接
                    SocketChannel sc = sscx.accept();
                    sc.configureBlocking(false);
                    sc.register(selc, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
                }
                //
                if (key.isReadable()) {
                    //获取通道
                    SocketChannel sc = (SocketChannel) key.channel();
                    //读取数据
                    ByteBuffer dst = ByteBuffer.allocate(1024);
                    sc.read(dst);
                    System.out.println(new String(dst.array(), 0, dst.position()));
                    //如果在同一个通道上注册,就会将之前注册的事件给注册
                    //key.interestOps()获取这个通道上的所有事件
                    //key.interestOps() - SelectionKey.OP_READ 表示在这个通道中所有事件列表中将READ事件去除
                    //实际上就是注销掉READ事件
                    sc.register(selc,
                            key.interestOps() - SelectionKey.OP_READ);
                    //方法二:异或: 1^1=0,1^0=1 相同为0,不同为1
                    sc.register(selc, key.interestOps() ^ SelectionKey.OP_READ);
                }
                //
                if (key.isWritable()) {
                    //获取通道
                    SocketChannel sc = (SocketChannel) key.channel();
                    //向客户端响应
                    sc.write(ByteBuffer.wrap("hi client".getBytes()));
                    //注销掉write事件
                    //异或: 1^1=0,1^0=1 相同为0,不同为1
                    sc.register(selc, key.interestOps() ^ SelectionKey.OP_WRITE);

                }
                //最后移除通道
                it.remove();

            }
        }
    }
}

客户端代码:

package com.apple.selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * This is Description
 *
 * @author apple
 * @date 2020/06/01
 */
public class Client {
    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketChannel.open();
        sc.connect(new InetSocketAddress("127.0.0.1", 8070));
        System.out.println("连接已经建立");

        //ByteBuffer.wrap 用于 知道 传输数据的大小 的场景下使用
        // ByteBuffer.allocate(size) 用于 不确定 传输数据的大小 的场景下使用
        //例如:服务端在接收客户端的请求时,是不知道有多少数据传输过来的,
        //同样,客户端接收服务端的响应时,也不知道多少数据传输过来
        sc.write(ByteBuffer.wrap("hello server".getBytes()));
        ByteBuffer dst = ByteBuffer.allocate(1024);
        sc.read(dst);
        System.out.println(new String(dst.array(), 0, dst.position()));
    }
}

 

posted @ 2020-06-05 10:24  alen-fly  阅读(138)  评论(0编辑  收藏  举报