zoukankan      html  css  js  c++  java
  • nio、bio区别,应运场景

    bio阻塞i/o

    a.面向流的,InputStream(),OuputStream字节输入流,字节输出流,Reader,Writer字符输入流,字符输出流
    b.阻塞的IO,比如Socket,它的底层用的BIO机制,accept()、connect()、write()调用时会产生阻塞。阻塞模型的局限性:不可能应对高并发、搞访问量的场景

    总结:BIO方式适用于连接数目比较小并且一次发送大量数据的场景,这种方式对服务器资源要求比较高,并发局限于应用中

    很简单转换成图型说明就是web浏览器发一个请求过来,web服务器就要new 一个线程来处理这个请求,这是传统的请求处理模型,这也就引来一个很大的问题,当请求越多,服务器端的启用线程也要越多,我们都知道linux(window)的文件句柄数有是限的,默认是1024,当然可以修改,上限好像是65536 ,(一个柄也相当于一个socket也相当于一个thread,linux查看文件句柄Unlimit -a) 其实在实际当中只要并发到1000上下响应请求就会很慢了,所以这种模型是有问题的,这种也就是同步阻塞IO编程(JAVA BIO)

    以下是简单的bio例子:

    Server端代码:

    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 
     * @author 
     * @date   2019-02-27
     */
    public class Server {
        public static void main(String[] args) throws IOException {
            ServerSocket socket = new ServerSocket();
            socket.bind(new InetSocketAddress(9999));
            while(true) {
                Socket sc = socket.accept();
                new Thread(new Accept(sc)).start();
            }
        }
    }
    class Accept implements Runnable{
        private Socket sc = null;
        public Accept(Socket sc) {
            sc = this.sc;
        }
        @Override
        public void run() {
            System.out.println("提供服务的线程id是:"+Thread.currentThread().getId());
        }
        
    }
    

    Clinet端代码

    /**
     * 
     * @author 
     * @date   2019-02-27
     */
    public class Client {
        public static void main(String[] args) throws IOException {
            Socket sc = new Socket();
            sc.connect(new InetSocketAddress( "127.0.0.1",9999),0);
            while(true) {
                
            }
        }
    }
    

    启动server端,然后多次启动client端,打印结果如下

    nio非阻塞i/o

    a.面向缓冲区的(Buffer),NIO是通过缓冲区去操作数据的,可以用缓冲区灵活操作数据,此外,NIO是面向通道的(Channel),在通道上,即可以读数据,也可以写数据

    b.NIO是非阻塞的IO,可以利用NIO处理高并发和高访问量的场景,NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java的nio实现。服务器需要支持超大量的长时间连接。比如10000个连接以上,并且每个客户端并不会频繁地发送太多数据

    首先,引入nio的3个相关概念:
    1> Buffer 缓冲区
    难用的buffer是一个抽象的对象,下面还有ByteBuffer,IntBuffer,LongBuffer等子类,相比老的IO将数据直接读/写到Stream对象,NIO是将所有数据都用到缓冲区处理,它本质上是一个数组,提供了位置,容量,上限等操作方法,还是直接看代码代码来得直接
    2>Channel 通道
    如自来水管一样,支持网络数据从Channel中读写,通道写流最大不同是通道是双向的,而流是一个方向上移动(InputStream/OutputStream),通道可用于读/写或读写同时进行,它还可以和下面要讲的selector结合起来,有多种状态位,方便selector去识别. 通道分两类,一:网络读写(selectableChannel),另一类是文件操作(FileChannel),我们常用的是上面例子中的网络读写!

    3>Selector 多路复用选择器
    它是神一样存在的东西,多路复用选择器提供选择已经就绪的任务的能力,也就是selector会不断轮询注册在其上的通道(Channel),如果某个通道发生了读写操作,这个通道处于就绪状态,会被selector轮询出来,然后通过selectionKey可以取得就绪的Channel集合,从而进行后续的IO操作.
    一个多路复用器(Selector)可以负责成千上万个Channel,没有上限,这也是JDK使用epoll代替了传统的selector实现,获得连接句柄没有限制.这也意味着我们只要一个线程负责selector的轮询,就可以接入成千上万个客户端,这是JDK,NIO库的巨大进步.

    以下是server端代码:

    
    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;
    
    public class NioServer {
        public static void main(String[] args) throws Exception {
            ServerSocketChannel ssc = ServerSocketChannel.open();
            ssc.configureBlocking(false);
            ssc.bind(new InetSocketAddress(6666));
            
            Selector selector = Selector.open();
            ssc.register(selector,SelectionKey.OP_ACCEPT);
            while(true) {
                selector.select();
                Set<SelectionKey> set = selector.selectedKeys();
                //获取事件结合的迭代器
                Iterator<SelectionKey> iter = set.iterator();
                while(iter.hasNext()) {
                    SelectionKey sk = iter.next();
                    if(sk.isAcceptable()) {
                        //证明有客户端请求建立回迁连接
                        ServerSocketChannel ss = (ServerSocketChannel)sk.channel();
                        //创建会话对象
                        SocketChannel sc = null;
                        while(sc==null) {
                            sc = ss.accept();
                        }
                        //设置为非阻塞模式
                        sc.configureBlocking(false);
                        //输出为客户端提供服务的线程ID
                        System.out.println("有客户端连入,负责提供服务的"
                                + "线程id:"+Thread.currentThread().getId());
                        //为sc注册read事件和write事件
                        sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
                    }
                    if(sk.isReadable()) {
                        //获取SocketChannel对象
                        SocketChannel sc = (SocketChannel)sk.channel();
                        ByteBuffer buf = ByteBuffer.allocate(10);
                        sc.read(buf);
                        System.out.println("服务器端独到的内容:"
                                +new String(buf.array()));
                        System.out.println("负责处理读请求的线程ID:"
                                +Thread.currentThread().getId());
                        sc.register(selector,sk.interestOps()&~SelectionKey.OP_READ);
                    }
                    if(sk.isWritable()) {
                        //获取SocketChannel对象
                        SocketChannel sc = (SocketChannel)sk.channel();
                        ByteBuffer buf = ByteBuffer.wrap("over".getBytes());
                        sc.write(buf);
                        System.out.println("负责处理写请求的线程ID:"
                                +Thread.currentThread().getId());
                        sc.register(selector,sk.interestOps()&~SelectionKey.OP_WRITE);
                    }
                    //删除该事件,目的:为了防止同一个事件被处理多次
                    iter.remove();
                }
                
            }
        }
    }   
    

    以下是client端代码

    
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    
    public class NIOClient {
        public static void main(String[] args) throws Exception {
            SocketChannel sc = SocketChannel.open();
            sc.configureBlocking(false);
            sc.connect(new InetSocketAddress("127.0.0.1", 6666));
            while(!sc.isConnected()) {
                sc.finishConnect();
            }
            ByteBuffer buf = ByteBuffer.wrap("helloworld".getBytes());
            sc.write(buf);
            ByteBuffer readBuf = ByteBuffer.allocate(9);
            Thread.sleep(1000);
            sc.read(readBuf);
            System.out.println("客户端读取到服务器发送过来的内容:"
                    +new String(readBuf.array()));
            while(true) {
                
            }
        }
    }
    

    这里补充一点aio,这个比NIO先进的技术,最终实现了
    netty
    这是神一样存在的java nio框架, 这个偏底层的东西,可能你接触较少却又无处不在,比如:

    在业界有一篇无法超越的netty入门文章,我也没这个能力超越,只能双手奉上,你们好好研读,必然学有所成!
    http://ifeve.com/netty5-user-guide/

    本文部分参考于:
    https://www.cnblogs.com/barrywxx/p/8430790.html

  • 相关阅读:
    拷贝某文件至某位置
    Java对象的序列化和反序列
    常见的RuntimeException异常有哪些
    array数组增删元素
    失眠怎么办
    构造函数和函数区别(关键的new操作符)
    匿名函数递归(arguments.callee)和命名函数递归
    localeCompare方法在chrome浏览器取值问题
    random()方法
    iframe 父子页面之间取值
  • 原文地址:https://www.cnblogs.com/XiOrang/p/10446931.html
Copyright © 2011-2022 走看看