NIO

在这里插入图片描述

NIO

概述

  • 称为No-Blocking IO或New IO,是从jdk1.4开始引进的一套新的IO,为所有基本类型(boolean除外)提供缓冲支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络

IO历史

  • BIO->NIO->AIO

IO操作模式

  • PIO

    • 所有IO操作由NICPU处理,CPU占用率比较高
  • DMA

    • CPU将IO操作的控制权交给DMA控制器,只能以固定的方式读写,CPU空闲做其他工作
  • Channel

    • 能执行有限通道指令的IO控制器,代替CPU管理控制外设
    • 通道有自己的指令系统,是一个协处理器,具有更强的独立处理数据输入和输出的能力

组成

Buffer缓冲区

  • 特点

    • 用于和NIO通道交互
    • 本质上是一块可以写入数据也可以读出数据的内存
  • 子类

    • ByteBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
    • CharBuffer
  • 核心方法

    • static ByteBuffer allocate(int capacity)

      • 在堆中开辟空间,间接缓冲区
    • static ByteBuffer allocateDirect(int capcity)

      • 在直接内存中开辟空间,直接缓冲区
    • ByteBuffer put(byte[] src)

      • 通过Buffer的put()方法写到Buffer里
    • Buffer flip()

      • 将Buffer从写模式切换到读模式。调用flip()方法会将position设为0,并将limit设为之前的position
    • ByteBuffer get(byte[] des)

      • 从Buffer中读数据
    • Buffer clear()

      • 将Buffer清空。将position设回为0,limit设为capacity
    • Buffer compact()

      • 将所有未读的数据拷贝到缓冲区起始处。将position设为最后一个未读元素的后面,limit设为capacity
  • 原理

    • 0<=position<=limit<=capacity
    • 初始化时position=0,limit=capacity
    • 添加元素时h[position++]=e
    • 切换模式时,limit=position,position=0
    • 压缩时position=未读元素后一个,limit = capacity
    • clear时,position=0,limit=capacity并没有将元素移除,数据处理遗留,装填元素后覆盖
  • 基本使用

    public class TestBuffer {
        public static void main(String[] args) {
            //1创建缓冲区,并使用Put方法存放数据
            ByteBuffer buffer=ByteBuffer.allocate(1024); // byte[] 1024
            //ByteBuffer byteBuffer=ByteBuffer.allocateDirect(1024);
            //2并使用Put方法存放数据
            String data="我爱java,I love Java";
            buffer.put(data.getBytes());
            //3反转缓冲区(把写入模式转成读取模式) flip();
            buffer.flip();
            //4读取
            //byte b = buffer.get();
            byte[] buf=new byte[buffer.limit()];
            buffer.get(buf);
            //System.out.println((char)b);
            System.out.println(new String(buf,0,buf.length));
            //5清空
            buffer.clear();
    
        }
    }
    

Channel通道接口

  • 类似流。数据可以从Buffer传到Channel,也可以从Channel传到Buffer

  • 实现类

    • FileChannel

      • 特点

        • FileChannel是一个连接到文件的通道。可以通过通道读写文件
        • FileChannel无法设置非阻塞模式
      • 创建方式

        • 使用文件字节流或RandomAccessFile类获得

          FileChannel fileChannel = new FileOutputStream("d:\\channel").getChannel();
          //      FileChannel fileChannel = new RandomAccessFile("d:\\channel.txt", "rw").getChannel();
          
        • 使用Channels工具类

          FileChannel writableByteChannel = (FileChannel) Channels.newChannel(new FileOutputStream("d:\\channel.txt"));
          
        • jdk1.7后使用FileChannel的open方法

          FileChannel channel=FileChannel.open(Paths.get("d:\\channel.txt"), StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.APPEND);
          
      • 解决中文乱码问题

        public static void read() throws Exception{
            //1 创建通道
            //FileChannel channel=FileChannel.open(Paths.get("d:\\channel.txt"),StandardOpenOption.READ);
            FileChannel channel = (FileChannel) Channels.newChannel(new FileInputStream("d:\\channel.txt"));
            //2 读取
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            //2.1创建解码器
            CharsetDecoder charsetDecoder = Charset.forName("utf-8").newDecoder();
            //2.2创建CharBuffer
            CharBuffer charBuffer=CharBuffer.allocate(1024);
            while(channel.read(buffer)>0){
                buffer.flip();
                //解码
                charsetDecoder.decode(buffer, charBuffer, false);
                charBuffer.flip();
                System.out.println(charBuffer.toString());
                //buffer.clear();
                buffer.compact();
                charBuffer.clear();
            }
            //3关闭
            channel.close();
        }
        
      • 内存映射文件

        public class MapFile {
            public static void main(String[] args) throws Exception{
                //1创建FileChannel
                FileChannel read=FileChannel.open(Paths.get("d:\\001.wmv"), StandardOpenOption.READ);
                FileChannel write=FileChannel.open(Paths.get("d:\\002.wmv"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
                //2直接内存映射文件
                MappedByteBuffer map1 = read.map(FileChannel.MapMode.READ_ONLY, 0, 1024*1024*100);
                //3内存映射文件写入文件
                write.write(map1);
                MappedByteBuffer map2=read.map(FileChannel.MapMode.READ_ONLY, 1024*1024*100, read.size()-(1024*1024*100));
                write.write(map2);
                //4关闭
                read.close();
                write.close();
                System.out.println("复制完毕");
            }
        }
        
    • ServerSocketChannel

    • SocketChannel

    • DatagramChannel

Selector选择器

  • 特点

    • Selector提供了询问通道是否已经准备好执行每个IO的能力
    • Selector允许单线程处理多个Channel
    • 仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通 道。这样会大量的减少线程之间上下文切换的开销
  • Selector类管理着一个被注册的通道集合的信息和他们的就绪状态。通道是和选择器一起注册的,并且使用选择器来更新通道的就绪状态

  • SelectionKey

    • 选择键封装了特定通道和特定选择器的注册关系

    • 选择键支持的四种操作类型

      • SelectionKey.OP_CONNECT
      • SelectionKey.OP_ACCEPT
      • SelectionKey.OP_READ
      • SelectionKey.OP_WRITE

与BIO的区别

NIO阻塞编程

public class Server {
    public static void main(String[] args) throws Exception {
        //1 创建服务器套接字通道
        ServerSocketChannel listener = ServerSocketChannel.open();
        //2 绑定地址和端口号
        listener.bind(new InetSocketAddress("10.0.139.49", 9999));
        //3 监听
        System.out.println("服务器已启动...");
        SocketChannel socketChannel = listener.accept();
        //4读取
        //4.1创建缓冲区
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //4.2读取
        socketChannel.read(buffer);
        //5处理数据
        buffer.flip();
        String data=new String(buffer.array(),0,buffer.limit());
        System.out.println(socketChannel.getRemoteAddress()+"说:"+data);
        buffer.clear();
        //6关闭
        socketChannel.close();
        listener.close();

    }
}

public class Client {
    public static void main(String[] args)throws Exception {
        //1创建SocketChannle
        SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("10.0.139.49", 9999));
        //2写入
        //2.1创建缓冲区
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        buffer.put("好久不见".getBytes());
        buffer.flip();
        //2.2写入
        socketChannel.write(buffer);
        buffer.clear();
        //3关闭通道
        socketChannel.close();
    }
}

NIO非阻塞编程

public class ChatServer {
    public static void main(String[] args) {
        ServerSocketChannel listener=null;
        try {
            //1创建ServerSocketChannel
            listener=ServerSocketChannel.open();
            //2绑定地址
            listener.bind(new InetSocketAddress("10.0.139.49", 8899));
            //3设置为非阻塞式模式
            listener.configureBlocking(false);
            //4创建Selector(选择器)
            Selector selector=Selector.open();
            //5把通道注册到选择器,并指定注册的事件
            listener.register(selector, SelectionKey.OP_ACCEPT);
            //6轮询(select()阻塞方法,没有事件发送阻塞)
            System.out.println("服务器已启动...");
            while(selector.select()>0){
                //7处理
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                while(it.hasNext()){
                    SelectionKey selectionKey = it.next();
                    //8判断selectionKey
                    if(selectionKey.isAcceptable()){
                        //表示有客户端请求,接收请求
                        SocketChannel socketChannel = listener.accept(); //不会阻塞
                        System.out.println(socketChannel.getRemoteAddress()+"进入了聊天室...");
                        //设置非阻塞式模式
                        socketChannel.configureBlocking(false);
                        //注册到selector
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }else if(selectionKey.isReadable()){
                        //读取数据
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        ByteBuffer buffer=ByteBuffer.allocate(1024);
                        int len=0;
                        try {
                            while((len=channel.read(buffer))>0){ //不会阻塞
                                buffer.flip();
                                String data=new String(buffer.array(),0,buffer.limit());
                                System.out.println(channel.getRemoteAddress()+"说:"+data);
                                buffer.clear();
                            }
                            //开启线程回复

                            if(len==-1){// 客户端关闭
                                System.out.println(channel.getRemoteAddress()+"退出了");
                                channel.close();
                            }
                        } catch (IOException e) {
                            System.out.println(channel.getRemoteAddress()+"异常退出了");
                            channel.close();
                        }
                    }
                    //已经处理过了键删除了
                    it.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                listener.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ChatClient {
    public static void main(String[] args) throws Exception {
        //1创建SocketChannel
        SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("10.0.139.49", 8899));
        //2设置为非阻塞模式
        socketChannel.configureBlocking(false);
        //3控制台录入并写出
        Scanner input=new Scanner(System.in);
        while(true){
            String data=input.nextLine();
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            buffer.put(data.getBytes());
            buffer.flip();
            socketChannel.write(buffer);
            buffer.clear();
            if(data.equals("886")){
                break;
            }
        }
        //4关闭
        socketChannel.close();

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值