摘要: 关于BIO和NIO的理解

最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO。下面是我结合网络资料自己总结的,为了节约时间图示随便画的,能达意就行。

简介:

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 
AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

BIO 
同步阻塞式IO,相信每一个学习过操作系统网络编程或者任何语言的网络编程的人都很熟悉,在while循环中服务端会调用accept方法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。 
如果BIO要能够同时处理多个客户端请求,就必须使用多线程,即每次accept阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续accept等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理,大概原理图就像这样: 

虽然此时服务器具备了高并发能力,即能够同时处理多个客户端请求了,但是却带来了一个问题,随着开启的线程数目增多,将会消耗过多的内存资源,导致服务器变慢甚至崩溃,NIO可以一定程度解决这个问题。

NIO 
同步非阻塞式IO,关键是采用了事件驱动的思想来实现了一个多路转换器。 
NIO与BIO最大的区别就是只需要开启一个线程就可以处理来自多个客户端的IO事件,这是怎么做到的呢? 
就是多路复用器,可以监听来自多个客户端的IO事件: 
A. 若服务端监听到客户端连接请求,便为其建立通信套接字(java中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字。 
B. 若服务端监听到来自已经创建了通信套接字的客户端发送来的数据,就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理。 
C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己时候有数据要发送。 

总之就是在一个线程中就可以调用多路复用接口(java中是select)阻塞同时监听来自多个客户端的IO请求,一旦有收到IO请求就调用对应函数处理。

各自应用场景

到这里你也许已经发现,一旦有请求到来(不管是几个同时到还是只有一个到),都会调用对应IO处理函数处理,所以:

(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。

(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。

附录:下面附上一个别人写的java NIO的例子。 
服务端:

  1. 1. package cn.nio;
  2. 2.
  3. 3. import java.io.IOException;
  4. 4. import java.net.InetSocketAddress;
  5. 5. import java.nio.ByteBuffer;
  6. 6. import java.nio.channels.SelectionKey;
  7. 7. import java.nio.channels.Selector;
  8. 8. import java.nio.channels.ServerSocketChannel;
  9. 9. import java.nio.channels.SocketChannel;
  10. 10. import java.util.Iterator;
  11. 11.
  12. 12. /**
  13. 13. * NIO服务端
  14. 14. *
  15. 15. */
  16. 16. public class NIOServer {
  17. 17.    //通道管理器
  18. 18.    private Selector selector;
  19. 19.
  20. 20.    /**
  21. 21.     * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
  22. 22.     * @param port  绑定的端口号
  23. 23.     * @throws IOException
  24. 24.     */
  25. 25.    public void initServer(int port) throws IOException {
  26. 26.        // 获得一个ServerSocket通道
  27. 27.        ServerSocketChannel serverChannel = ServerSocketChannel.open();
  28. 28.        // 设置通道为非阻塞
  29. 29.        serverChannel.configureBlocking(false);
  30. 30.        // 将该通道对应的ServerSocket绑定到port端口
  31. 31.        serverChannel.socket().bind(new InetSocketAddress(port));
  32. 32.        // 获得一个通道管理器
  33. 33.        this.selector = Selector.open();
  34. 34.        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
  35. 35.        //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
  36. 36.        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  37. 37.    }
  38. 38.
  39. 39.    /**
  40. 40.     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  41. 41.     * @throws IOException
  42. 42.     */
  43. 43.    @SuppressWarnings("unchecked")
  44. 44.    public void listen() throws IOException {
  45. 45.        System.out.println("服务端启动成功!");
  46. 46.        // 轮询访问selector
  47. 47.        while (true) {
  48. 48.            //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
  49. 49.            selector.select();
  50. 50.            // 获得selector中选中的项的迭代器,选中的项为注册的事件
  51. 51.            Iterator ite = this.selector.selectedKeys().iterator();
  52. 52.            while (ite.hasNext()) {
  53. 53.                SelectionKey key = (SelectionKey) ite.next();
  54. 54.                // 删除已选的key,以防重复处理
  55. 55.                ite.remove();
  56. 56.                // 客户端请求连接事件
  57. 57.                if (key.isAcceptable()) {
  58. 58.                    ServerSocketChannel server = (ServerSocketChannel) key
  59. 59.                            .channel();
  60. 60.                    // 获得和客户端连接的通道
  61. 61.                    SocketChannel channel = server.accept();
  62. 62.                    // 设置成非阻塞
  63. 63.                    channel.configureBlocking(false);
  64. 64.
  65. 65.                    //在这里可以给客户端发送信息哦
  66. 66.                    channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
  67. 67.                    //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
  68. 68.                    channel.register(this.selector, SelectionKey.OP_READ);
  69. 69.
  70. 70.                    // 获得了可读的事件
  71. 71.                } else if (key.isReadable()) {
  72. 72.                        read(key);
  73. 73.                }
  74. 74.
  75. 75.            }
  76. 76.
  77. 77.        }
  78. 78.    }
  79. 79.    /**
  80. 80.     * 处理读取客户端发来的信息 的事件
  81. 81.     * @param key
  82. 82.     * @throws IOException
  83. 83.     */
  84. 84.    public void read(SelectionKey key) throws IOException{
  85. 85.        // 服务器可读取消息:得到事件发生的Socket通道
  86. 86.        SocketChannel channel = (SocketChannel) key.channel();
  87. 87.        // 创建读取的缓冲区
  88. 88.        ByteBuffer buffer = ByteBuffer.allocate(10);
  89. 89.        channel.read(buffer);
  90. 90.        byte[] data = buffer.array();
  91. 91.        String msg = new String(data).trim();
  92. 92.        System.out.println("服务端收到信息:"+msg);
  93. 93.        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
  94. 94.        channel.write(outBuffer);// 将消息回送给客户端
  95. 95.    }
  96. 96.
  97. 97.    /**
  98. 98.     * 启动服务端测试
  99. 99.     * @throws IOException
  100. 100.     */
  101. 101.    public static void main(String[] args) throws IOException {
  102. 102.        NIOServer server = new NIOServer();
  103. 103.        server.initServer(8000);
  104. 104.        server.listen();
  105. 105.    }
  106. 106.
  107. 107. }

客户端:

  1. 1. package cn.nio;
  2. 2.
  3. 3. import java.io.IOException;
  4. 4. import java.net.InetSocketAddress;
  5. 5. import java.nio.ByteBuffer;
  6. 6. import java.nio.channels.SelectionKey;
  7. 7. import java.nio.channels.Selector;
  8. 8. import java.nio.channels.SocketChannel;
  9. 9. import java.util.Iterator;
  10. 10.
  11. 11. /**
  12. 12. * NIO客户端
  13. 13. *
  14. 14. */
  15. 15. public class NIOClient {
  16. 16.    //通道管理器
  17. 17.    private Selector selector;
  18. 18.
  19. 19.    /**
  20. 20.     * 获得一个Socket通道,并对该通道做一些初始化的工作
  21. 21.     * @param ip 连接的服务器的ip
  22. 22.     * @param port  连接的服务器的端口号
  23. 23.     * @throws IOException
  24. 24.     */
  25. 25.    public void initClient(String ip,int port) throws IOException {
  26. 26.        // 获得一个Socket通道
  27. 27.        SocketChannel channel = SocketChannel.open();
  28. 28.        // 设置通道为非阻塞
  29. 29.        channel.configureBlocking(false);
  30. 30.        // 获得一个通道管理器
  31. 31.        this.selector = Selector.open();
  32. 32.
  33. 33.        // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
  34. 34.        //用channel.finishConnect();才能完成连接
  35. 35.        channel.connect(new InetSocketAddress(ip,port));
  36. 36.        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
  37. 37.        channel.register(selector, SelectionKey.OP_CONNECT);
  38. 38.    }
  39. 39.
  40. 40.    /**
  41. 41.     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  42. 42.     * @throws IOException
  43. 43.     */
  44. 44.    @SuppressWarnings("unchecked")
  45. 45.    public void listen() throws IOException {
  46. 46.        // 轮询访问selector
  47. 47.        while (true) {
  48. 48.            selector.select();
  49. 49.            // 获得selector中选中的项的迭代器
  50. 50.            Iterator ite = this.selector.selectedKeys().iterator();
  51. 51.            while (ite.hasNext()) {
  52. 52.                SelectionKey key = (SelectionKey) ite.next();
  53. 53.                // 删除已选的key,以防重复处理
  54. 54.                ite.remove();
  55. 55.                // 连接事件发生
  56. 56.                if (key.isConnectable()) {
  57. 57.                    SocketChannel channel = (SocketChannel) key
  58. 58.                            .channel();
  59. 59.                    // 如果正在连接,则完成连接
  60. 60.                    if(channel.isConnectionPending()){
  61. 61.                        channel.finishConnect();
  62. 62.
  63. 63.                    }
  64. 64.                    // 设置成非阻塞
  65. 65.                    channel.configureBlocking(false);
  66. 66.
  67. 67.                    //在这里可以给服务端发送信息哦
  68. 68.                    channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
  69. 69.                    //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
  70. 70.                    channel.register(this.selector, SelectionKey.OP_READ);
  71. 71.
  72. 72.                    // 获得了可读的事件
  73. 73.                } else if (key.isReadable()) {
  74. 74.                        read(key);
  75. 75.                }
  76. 76.
  77. 77.            }
  78. 78.
  79. 79.        }
  80. 80.    }
  81. 81.    /**
  82. 82.     * 处理读取服务端发来的信息 的事件
  83. 83.     * @param key
  84. 84.     * @throws IOException
  85. 85.     */
  86. 86.    public void read(SelectionKey key) throws IOException{
  87. 87.        //和服务端的read方法一样
  88. 88.    }
  89. 89.
  90. 90.
  91. 91.    /**
  92. 92.     * 启动客户端测试
  93. 93.     * @throws IOException
  94. 94.     */
  95. 95.    public static void main(String[] args) throws IOException {
  96. 96.        NIOClient client = new NIOClient();
  97. 97.        client.initClient("localhost",8000);
  98. 98.        client.listen();
  99. 99.    }
  100. 100.
  101. 101. }

http://blog.csdn.net/jiyiqinlovexx/article/details/42619097 

关于BIO和NIO的理解的更多相关文章

  1. Java提高班(五)深入理解BIO、NIO、AIO

    导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...

  2. BIO与NIO、AIO的区别(这个容易理解)

    转自:http://blog.csdn.net/skiof007/article/details/52873421 BIO与NIO.AIO的区别(这个容易理解) IO的方式通常分为几种,同步阻塞的BI ...

  3. 从实践角度重新理解BIO和NIO

    前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整,但是整个看下来之后,自己对NIO还是一知半解的状态,所以这 ...

  4. Java核心(一)深入理解BIO、NIO、AIO

    目标: BIO.NIO.AIO 的区别是什么? 同/异步.阻/非阻塞的区别是什么? 文件读写最优雅的实现方式是什么? NIO 如何实现多路复用功能? 一,IO的介绍: (1)IO的全称其实是:Inpu ...

  5. [转帖]JAVA BIO与NIO、AIO的区别(这个容易理解)

    JAVA BIO与NIO.AIO的区别(这个容易理解) https://blog.csdn.net/ty497122758/article/details/78979302 2018-01-05 11 ...

  6. Java中BIO,NIO,AIO的理解

    在高性能的I/O体系设计中,有几个概念常常会使我们感到迷惑不解.具体如下: 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7  ...

  7. 深入理解BIO、NIO、AIO

    导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...

  8. BIO、NIO、AIO --- 个人理解

    1.前言 什么是 BIO.NIO.AIO  ,不难看出,都是共同的字符IO , IO的意思是input output  ,即输入输出 , 那么 B . N .A 分别指不同的io模型 ,而io又分为 ...

  9. Java BIO、NIO、AIO 学习(转)

    转自 http://stevex.blog.51cto.com/4300375/1284437 先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Ja ...

随机推荐

  1. Permission Policies

    The Permission Policy determines Security System behavior when there are no explicitly specified per ...

  2. CSS文本溢出处理方式

    1. 单行文本溢出省略号效果 .ellipsis { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; } <div cl ...

  3. Docker部署Redis容器

    从仓库下载镜像 sudo docker pull redis   创建容器(前提:将redis.conf文件放入到/Users/chengang/docker/redis目录里面) docker ru ...

  4. tomcat启动项目的时候不报错而且启动的很快

    最后发现是tomcat部署项目的时候,并没有将一部分文件复制到tomcat的目录下 方法 将没有添加的目录 Finish

  5. 浏览器初始页面设置及被hao123劫持解决办法

    最近在用浏览器时打开初始页面都是hao123,喵喵喜欢简单干净的页面,就去设置初始页面. 此处放置初始页面参考(并不太难): https://jingyan.baidu.com/article/11c ...

  6. OTRS

    更新OTRS root@localhost密码: sudo -u otrs /opt/otrs/bin/otrs.Console.pl Admin::User::SetPassword root@lo ...

  7. 出现 org.springframework.beans.factory.BeanCreationException 异常的原因及解决方法

    1 异常描述 在从 SVN 检出项目并配置完成后,启动 Tomcat 服务器,报出如下错误: 2 异常原因 通过观察上图中被标记出来的异常信息,咱们可以知道 org.springframework.b ...

  8. Daily Scrum (2015/11/3)

    今天我们的爬虫能在pc上成功运行并且把所爬取的数据存到服务器上了!我们已经搭建好数据库,把相关信息存到数据库中,并把数据存到D盘里共享给数据处理小组使用. 成员 今日工作 时间 明日工作 符美潇 完成 ...

  9. 第一节 Linux系统简介

    一.Linux定义 Linux 是一个操作系统,就像你多少已经了解的 Windows(xp,7,8)和 Max OS. 操作系统在整个计算机系统中的角色: Linux 是系统调用和内核那两层,直观的来 ...

  10. P2P通讯原理

    1.简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信.这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还 ...