打开NIO世界的大门

一.概述

  1.NIO是什么?

    NIO,有人叫New IO,因为它是jdk1.4之后才加进来的。但是其实更加准确的定义是non-blocking IO,就是非阻塞IO。总所周知,传统的IO操作是阻塞的,效率不高,浪费cpu的性能。NIO的到来就解决了这个问题。

  2.BIO,NIO,AIO?

    BIO:同步阻塞,其实就是BlockingIO,也就是传统的那些IO操作。同步是说,当服务器执行去磁盘执行read操作时,要自己去看有没有成功获取数据,然后如果没有数据返回就一直在等,边等边查数据是否准备好了,服务器(程序)是干不了其他事情的。一般以前的IO为了处理高并发,都是为每一个IO操作请求建立一个线程来干IO操作,好让服务器cpu可以干其他事情。  

    NIO:同步非阻塞,同步是说还是一样,程序要定时去读取磁盘,这里有选择器里面的轮询搞掂,然后非阻塞是说执行read操作时,不过有没有数据,都会马上返回,好让服务器(程序)可以干其他线程,这样服务器就不用创建很多线程去操作IO了。

    AIO:异步非阻塞,AIO是发出IO请求后,由操作系统自己去获取IO权限并进行IO操作,成功了就通知程序,说数据搞好了,这个时候程序再调用回调函数处理数据,异步是指由操作系统通知程序,非阻塞是调用read方法没有数据也返回,程序继续往下执行。

二.NIO三大利器

  1.缓冲区

      核心方法:

           allocate:分配指定空间大小的缓冲区。

           put和get:获取缓冲区的数据。

           rewind:重置position位置,读写模式都行。

           flip:写转读模式,原来写的位置position变为读的上限limit,position置0.

           clear:limit变为capactity,position变为0,写模式。

      核心变量:

          capactity:最大容量,一般为常量。

          limit:数据有效的上限。

          mark:记录当前位置position。

          position:当前位置。

       缓冲分类:

          直接缓冲:是指分配到物理内存的缓冲区,通过allocateDirect来分配

          间接缓冲:分配在堆内的缓冲,其实就是数组啦,通过allocate分配

          判别方法:isDirect();

  2.通道

    1.作用:负责缓冲区的数据的传输,本身不包含数据,要和Buffer一起共用。

    2.实现类:FileChannel,SocketChannel,ServerSocketChanel,DatapramChannel。

    3.具体使用:通过getChannel获取一个Channel

          本地IO:FileInputStream,FileOutputStream,RandomAccess

          网络IO:Socket,ServerSocket,DatagramSocket

  3.Selector选择器

    1.作用:一个多路复用选择器,通过轮询监听通道的信息,它是NIO实现非阻塞的关键。可以看一下NIO的HelloWord里面它的作用,代码如下:

      //4.获取选择器
        Selector selector = Selector.open();
        //5.注册通道到选择器
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6.轮询式获取选择器准备就绪的事件
        while(selector.select()>0){
            //7.获取选择器里面的事件的
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                //8.获取选择器里面的事件
                SelectionKey sk = iterator.next();
                if (sk.isAcceptable()){
                    //9.接收客户端的socket
                    SocketChannel clientAcceptChannel = serverSocketChannel.accept();
                    //10.设置为非阻塞模式
                    clientAcceptChannel.configureBlocking(false);
                    //11.设置这个socketChannel为准备读状态,注册到选择器上面
                    clientAcceptChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    //12.获取当前选择器上面状态为可读的通道
                    SocketChannel socketReadChannel = (SocketChannel) sk.channel();
                    //13.开始往读取客户端那个通道的数据
                    ByteBuffer readClientBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while((len=socketReadChannel.read(readClientBuffer))>0){
                        readClientBuffer.flip();
                        //14.输入客户端传的数据到控制台
                        System.out.println(new String(readClientBuffer.array(),0,len));
                        readClientBuffer.clear();
                    }

                }
                iterator.remove();
            }

         

四.NIO实战

   下面这个NIO实战小Demo是模拟一个简易的聊天室,有服务端程序Server,也有Client程序,客户端创建通道,缓冲区,从键盘输入获取数据传送给服务端。服务端则是一样,创建通道,绑定端口,注册通道到选择器,然后最重要是轮询,把注册到选择器里面的通道拿出来进行状态判断,进行相应的操作,具体代码看下面。  

    Server端

public class Server {
    public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.receive();
    }

    public void receive() throws IOException {
        //1.创建通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.切换到非阻塞模式
        serverSocketChannel.configureBlocking(false);
        //3.绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8089));
        //4.获取选择器
        Selector selector = Selector.open();
        //5.注册通道到选择器
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6.轮询式获取选择器准备就绪的事件
        while(selector.select()>0){
            //7.获取选择器里面的事件的
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                //8.获取选择器里面的事件
                SelectionKey sk = iterator.next();
                if (sk.isAcceptable()){
                    //9.接收客户端的socket
                    SocketChannel clientAcceptChannel = serverSocketChannel.accept();
                    //10.设置为非阻塞模式
                    clientAcceptChannel.configureBlocking(false);
                    //11.设置这个socketChannel为准备读状态,注册到选择器上面
                    clientAcceptChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    //12.获取当前选择器上面状态为可读的通道
                    SocketChannel socketReadChannel = (SocketChannel) sk.channel();
                    //13.开始往读取客户端那个通道的数据
                    ByteBuffer readClientBuffer = ByteBuffer.allocate(1024);
                    int len = 0;
                    while((len=socketReadChannel.read(readClientBuffer))>0){
                        readClientBuffer.flip();
                        //14.输入客户端传的数据到控制台
                        System.out.println(new String(readClientBuffer.array(),0,len));
                        readClientBuffer.clear();
                    }

                }
                iterator.remove();
            }

        }
    }
}

    Client端

public class Client  {
    public static void main(String[] args) throws IOException {
        Client client = new Client();
        client.send();
    }

    private  void send() throws IOException{
        //1.获取SocketChannel通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8089));
        //2.切换到非阻塞模式
        socketChannel.configureBlocking(false);
        //3.创建缓冲区
        ByteBuffer clientBuffer = ByteBuffer.allocate(1024);
        //4.发送数据给服务端
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
            String str = scanner.next();
            clientBuffer.put((new Date().toString()+"-----锐鑫-----"+"\n"+str).getBytes());
            clientBuffer.flip();
            socketChannel.write(clientBuffer);
            clientBuffer.clear();
        }
        socketChannel.close();

    }

}

猜你喜欢

转载自www.cnblogs.com/JimmyFanHome/p/9992270.html
今日推荐