NIO编程入门

        NIO编程:

                        NIO的三个核心部位:Channel(通道),Buffer(缓冲区),Selector(选择器)

NIO和BIO的比较

        1:BIO以流的方式处理数据,而NIO以块的方式处理数据,快I/O的效率比流I/O的效率高很多

        2:BIO是阻塞,NIO是非阻塞

        3BIO基于字节流和字符串流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓存区,或者从缓存区写入通道中,Selector(选择器)用于监听多个通道(比如:连接请求,数据到达等),因此使用单个线程就可以监视多个客户端通道

NIO                                                    BIO
面向缓冲区(Buffer)                      面向流(Stream)
非阻塞(Non Blocking IO)            阻塞IO(Blocking IO)
选择器(Selectors)    

NIO三个组件:

        Buffer缓存区:临时储存数据的内存空间,可以立即成一个数组

buffer的基本类型

        ByteBuffer,CherBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer

buffer的基本使用

        创建一个内存空间

ByteBuffer buf = ByteBuffer.allocate(1024);

        可以像Buffer放入数据

 buf.put("你好同学".getBytes(StandardCharsets.UTF_8));

        在读取buffer之前进行反转

buf.flip();//反转

        反转之后才可以读取数据

byte[] result  = new byte[buf.limit()];//使用limit创建读取数据的数组,不能超过limit
buf.get(result);

         设置可以重复读取

buf.rewind();

        需要重新读取数据的时候最好清理一下原有的数据

buf.clear();

Channel通道 :

        channel的主要作用就是从buffer中读取或者写入数据

        channel的使用和流有点类似

                通道即可读,也可写,流可以分成写入和输出流

                通道是可以设置成非阻塞的,流是阻塞的

                通道主要是从buffer中读取数据

各种通道

        FileChannel:文件通道

        DatagramChannel:UDP通道

        SocketChannel:一般的网络通信通道

        ServerSocketChannel:专门监听连接Socket通道

文件通道的基本使用

        实现文件写入数据

        File file = new File("a.txt");
        String data ="测试数据";
        //创建一个buffer,将数据放入到buffer中
        ByteBuffer buffer  = ByteBuffer.allocate(1024);
        //将数据先放入到buffer中
        buffer.put(data.getBytes(StandardCharsets.UTF_8));
        //马上翻转
        buffer.flip();

        //通过文件打开输出流
        FileOutputStream fos = new FileOutputStream(file);
        //通过流获取一个通道
        FileChannel channel = fos.getChannel();
        //将buffer中的数据写入到文件中
        channel.write(buffer);
        //关闭通道
        channel.close();

从文件中读取数据

    File file = new File("a.txt");
        //通过流打开channel
        FileInputStream fis = new FileInputStream(file);
        FileChannel fileChannel = fis.getChannel();
        //创建buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //将文件中的数据,通过channel读取到buffer中
        fileChannel.read(buffer);
        buffer.flip();
        //将buffer中的数据输出
        System.out.println(new String(buffer.array(),0,buffer.remaining()));

 文件的复制功能

        //将a复制到b
        File afile = new File("a.txt");
        File bfile = new File("b.txt");

        //打开流
        FileInputStream fis = new FileInputStream(afile);
        FileOutputStream fos = new FileOutputStream(bfile);
        //打开通道
        FileChannel isChannel = fis.getChannel();
        FileChannel osChannel = fos.getChannel();
        //创建一个buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int flag =0;
        while(flag !=-1){
            buffer.clear();//将缓存重置成初始状态
            flag = isChannel.read(buffer);
            buffer.flip();//反转
            osChannel.write(buffer);
        }
        isChannel.close();
        osChannel.close();
        fis.close();
        fos.close();
        System.out.println("复制成功");

Selector选择器

        是整个NIO的核心,尤其实现了多路复用的功能

        将所有的Channel注册到selector上,由selector遍历所有出现了数据变化的channel来执行操作

        实现socket连接的操作

                服务端:

public static void main(String[] args) throws IOException {
        //创建一个Selector
        Selector selector = Selector.open();
        //将需要监听的channel注册到selector之上
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //让ServerSocketChannel绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8989));
        //设置channel是非阻塞
        serverSocketChannel.configureBlocking(false);
        //主要用户接收连接的用户的
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //接收进入到用户
        while(selector.select()>0){//会阻塞代码
            SocketChannel socketChannel = serverSocketChannel.accept();
            System.out.println("有人进来了");
        }

    }

 客户端直接连接服务端

public static void main(String[] args) throws IOException {
        //打开连接服务器的通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
}

 单项的消息发送

        服务器端通过selector判断channel变化情况

    public static void main(String[] args) throws IOException {
        //创建一个Selector
        Selector selector = Selector.open();
        //将需要监听的channel注册到selector之上
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //让ServerSocketChannel绑定端口
        serverSocketChannel.bind(new InetSocketAddress(8989));
        //设置channel是非阻塞
        serverSocketChannel.configureBlocking(false);
        //主要用户接收连接的用户的
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动了");
        //接收进入到用户
        while(selector.select()>0){//会阻塞代码
            //每个有监听的channel发生变化的时候,会将所有发送的状态放入到一个集合中
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while(it.hasNext()){
                SelectionKey selectionKey = it.next();//获取当前channel变化的key
                if(selectionKey.isAcceptable()){//判断是有新的连接了
                    System.out.println("有人进来了");
                    //获取连接的channel
                    SocketChannel socketChannel = serverSocketChannel.accept();//非阻塞
                    socketChannel.configureBlocking(false);
                    //将channel注册到selector
                    socketChannel.register(selector,SelectionKey.OP_READ);
                }else if(selectionKey.isReadable()){//判断是读取状态
                    //获取当前key对应的channel
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    channel.read(buffer);//非阻塞
                    buffer.flip();
                    System.out.println(new String(buffer.array(),0,buffer.remaining()));
                }
            }
            //移除当前key的迭代器
            it.remove();
        }
    }

客户端发送请求

   public static void main(String[] args) throws IOException {
        //打开连接服务器的通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        Scanner scanner = new Scanner(System.in);
        while (true){
            String msg = scanner.next();
            //将msg放入到buffer中
            buffer.put(msg.getBytes(StandardCharsets.UTF_8));
            buffer.flip();
            //通过channel将数据发送
            socketChannel.write(buffer);
            buffer.clear();//重置游标
        }
    }

 小结:

        BIO:

                阻塞编程,代码必须一行一行执行,简单好操作

                在处理并发问题的情况下,需要被每个连接都安排一个线程,极大的影响服务器的性能

                适用于线程固定的,对服务器资源要求较高的场景

        NIO

                非阻塞编程,主要使用的是多路复用的概念,让每个线程通信消息放入到buffer缓存中,然后连接buffer缓存的channel注册到selector上

                selector进行要轮询状态,会监听所有的注册的channel变化情况,一但有发生变化就对其进行处理,

                selector在监听的过程中还是会阻塞

                适合线程伸缩大,并发高,对资源要求不多的情况下,处理业务时间要短

        AIO

                异步非编程,在NIO的基础上,并不是通过轮询/监听的方式,而是让buffer发送变化之后,主动通知系统进行处理

                将缓存的数据储存在操作系统 的内核中,处理完毕之后在通知用户线程

                适合对资源要求高,处理业务时间长的操作,比如文件上传       

                

                

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值