关于BIO和NIO的理解
摘要: 关于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. package cn.nio;
- 2.
- 3. import java.io.IOException;
- 4. import java.net.InetSocketAddress;
- 5. import java.nio.ByteBuffer;
- 6. import java.nio.channels.SelectionKey;
- 7. import java.nio.channels.Selector;
- 8. import java.nio.channels.ServerSocketChannel;
- 9. import java.nio.channels.SocketChannel;
- 10. import java.util.Iterator;
- 11.
- 12. /**
- 13. * NIO服务端
- 14. *
- 15. */
- 16. public class NIOServer {
- 17. //通道管理器
- 18. private Selector selector;
- 19.
- 20. /**
- 21. * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
- 22. * @param port 绑定的端口号
- 23. * @throws IOException
- 24. */
- 25. public void initServer(int port) throws IOException {
- 26. // 获得一个ServerSocket通道
- 27. ServerSocketChannel serverChannel = ServerSocketChannel.open();
- 28. // 设置通道为非阻塞
- 29. serverChannel.configureBlocking(false);
- 30. // 将该通道对应的ServerSocket绑定到port端口
- 31. serverChannel.socket().bind(new InetSocketAddress(port));
- 32. // 获得一个通道管理器
- 33. this.selector = Selector.open();
- 34. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
- 35. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
- 36. serverChannel.register(selector, SelectionKey.OP_ACCEPT);
- 37. }
- 38.
- 39. /**
- 40. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
- 41. * @throws IOException
- 42. */
- 43. @SuppressWarnings("unchecked")
- 44. public void listen() throws IOException {
- 45. System.out.println("服务端启动成功!");
- 46. // 轮询访问selector
- 47. while (true) {
- 48. //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
- 49. selector.select();
- 50. // 获得selector中选中的项的迭代器,选中的项为注册的事件
- 51. Iterator ite = this.selector.selectedKeys().iterator();
- 52. while (ite.hasNext()) {
- 53. SelectionKey key = (SelectionKey) ite.next();
- 54. // 删除已选的key,以防重复处理
- 55. ite.remove();
- 56. // 客户端请求连接事件
- 57. if (key.isAcceptable()) {
- 58. ServerSocketChannel server = (ServerSocketChannel) key
- 59. .channel();
- 60. // 获得和客户端连接的通道
- 61. SocketChannel channel = server.accept();
- 62. // 设置成非阻塞
- 63. channel.configureBlocking(false);
- 64.
- 65. //在这里可以给客户端发送信息哦
- 66. channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
- 67. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
- 68. channel.register(this.selector, SelectionKey.OP_READ);
- 69.
- 70. // 获得了可读的事件
- 71. } else if (key.isReadable()) {
- 72. read(key);
- 73. }
- 74.
- 75. }
- 76.
- 77. }
- 78. }
- 79. /**
- 80. * 处理读取客户端发来的信息 的事件
- 81. * @param key
- 82. * @throws IOException
- 83. */
- 84. public void read(SelectionKey key) throws IOException{
- 85. // 服务器可读取消息:得到事件发生的Socket通道
- 86. SocketChannel channel = (SocketChannel) key.channel();
- 87. // 创建读取的缓冲区
- 88. ByteBuffer buffer = ByteBuffer.allocate(10);
- 89. channel.read(buffer);
- 90. byte[] data = buffer.array();
- 91. String msg = new String(data).trim();
- 92. System.out.println("服务端收到信息:"+msg);
- 93. ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
- 94. channel.write(outBuffer);// 将消息回送给客户端
- 95. }
- 96.
- 97. /**
- 98. * 启动服务端测试
- 99. * @throws IOException
- 100. */
- 101. public static void main(String[] args) throws IOException {
- 102. NIOServer server = new NIOServer();
- 103. server.initServer(8000);
- 104. server.listen();
- 105. }
- 106.
- 107. }
客户端:
- 1. package cn.nio;
- 2.
- 3. import java.io.IOException;
- 4. import java.net.InetSocketAddress;
- 5. import java.nio.ByteBuffer;
- 6. import java.nio.channels.SelectionKey;
- 7. import java.nio.channels.Selector;
- 8. import java.nio.channels.SocketChannel;
- 9. import java.util.Iterator;
- 10.
- 11. /**
- 12. * NIO客户端
- 13. *
- 14. */
- 15. public class NIOClient {
- 16. //通道管理器
- 17. private Selector selector;
- 18.
- 19. /**
- 20. * 获得一个Socket通道,并对该通道做一些初始化的工作
- 21. * @param ip 连接的服务器的ip
- 22. * @param port 连接的服务器的端口号
- 23. * @throws IOException
- 24. */
- 25. public void initClient(String ip,int port) throws IOException {
- 26. // 获得一个Socket通道
- 27. SocketChannel channel = SocketChannel.open();
- 28. // 设置通道为非阻塞
- 29. channel.configureBlocking(false);
- 30. // 获得一个通道管理器
- 31. this.selector = Selector.open();
- 32.
- 33. // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
- 34. //用channel.finishConnect();才能完成连接
- 35. channel.connect(new InetSocketAddress(ip,port));
- 36. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
- 37. channel.register(selector, SelectionKey.OP_CONNECT);
- 38. }
- 39.
- 40. /**
- 41. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
- 42. * @throws IOException
- 43. */
- 44. @SuppressWarnings("unchecked")
- 45. public void listen() throws IOException {
- 46. // 轮询访问selector
- 47. while (true) {
- 48. selector.select();
- 49. // 获得selector中选中的项的迭代器
- 50. Iterator ite = this.selector.selectedKeys().iterator();
- 51. while (ite.hasNext()) {
- 52. SelectionKey key = (SelectionKey) ite.next();
- 53. // 删除已选的key,以防重复处理
- 54. ite.remove();
- 55. // 连接事件发生
- 56. if (key.isConnectable()) {
- 57. SocketChannel channel = (SocketChannel) key
- 58. .channel();
- 59. // 如果正在连接,则完成连接
- 60. if(channel.isConnectionPending()){
- 61. channel.finishConnect();
- 62.
- 63. }
- 64. // 设置成非阻塞
- 65. channel.configureBlocking(false);
- 66.
- 67. //在这里可以给服务端发送信息哦
- 68. channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
- 69. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
- 70. channel.register(this.selector, SelectionKey.OP_READ);
- 71.
- 72. // 获得了可读的事件
- 73. } else if (key.isReadable()) {
- 74. read(key);
- 75. }
- 76.
- 77. }
- 78.
- 79. }
- 80. }
- 81. /**
- 82. * 处理读取服务端发来的信息 的事件
- 83. * @param key
- 84. * @throws IOException
- 85. */
- 86. public void read(SelectionKey key) throws IOException{
- 87. //和服务端的read方法一样
- 88. }
- 89.
- 90.
- 91. /**
- 92. * 启动客户端测试
- 93. * @throws IOException
- 94. */
- 95. public static void main(String[] args) throws IOException {
- 96. NIOClient client = new NIOClient();
- 97. client.initClient("localhost",8000);
- 98. client.listen();
- 99. }
- 100.
- 101. }
http://blog.csdn.net/jiyiqinlovexx/article/details/42619097
关于BIO和NIO的理解的更多相关文章
- Java提高班(五)深入理解BIO、NIO、AIO
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...
- BIO与NIO、AIO的区别(这个容易理解)
转自:http://blog.csdn.net/skiof007/article/details/52873421 BIO与NIO.AIO的区别(这个容易理解) IO的方式通常分为几种,同步阻塞的BI ...
- 从实践角度重新理解BIO和NIO
前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整,但是整个看下来之后,自己对NIO还是一知半解的状态,所以这 ...
- Java核心(一)深入理解BIO、NIO、AIO
目标: BIO.NIO.AIO 的区别是什么? 同/异步.阻/非阻塞的区别是什么? 文件读写最优雅的实现方式是什么? NIO 如何实现多路复用功能? 一,IO的介绍: (1)IO的全称其实是:Inpu ...
- [转帖]JAVA BIO与NIO、AIO的区别(这个容易理解)
JAVA BIO与NIO.AIO的区别(这个容易理解) https://blog.csdn.net/ty497122758/article/details/78979302 2018-01-05 11 ...
- Java中BIO,NIO,AIO的理解
在高性能的I/O体系设计中,有几个概念常常会使我们感到迷惑不解.具体如下: 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步非阻塞? 7 ...
- 深入理解BIO、NIO、AIO
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...
- BIO、NIO、AIO --- 个人理解
1.前言 什么是 BIO.NIO.AIO ,不难看出,都是共同的字符IO , IO的意思是input output ,即输入输出 , 那么 B . N .A 分别指不同的io模型 ,而io又分为 ...
- Java BIO、NIO、AIO 学习(转)
转自 http://stevex.blog.51cto.com/4300375/1284437 先来个例子理解一下概念,以银行取款为例: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Ja ...
随机推荐
- 【转】将Centos的yum源更换为国内的阿里云源
摘要: 阿里云是最近新出的一个镜像源.得益于阿里云的高速发展,这么大的需求,肯定会推出自己的镜像源. 阿里云Linux安装镜像源地址:http://mirrors.aliyun.com/ CentOS ...
- Laya中的Image、Texture、WebGLImage
Image Image是Laya的一个UI组件,继承自Component. Image.bitmap属性,是AutoBitmap类型:AutoBitmap继承自Graphics,负责处理图片九宫格逻辑 ...
- Java字符串分割
java中字符串的分割函数,split("你想要分割的字符", 你想要最多分割为多少段,正整数) 注意事项: 1.分割特殊字符考虑转义字符的使用.如: . \ | 2.第二个参数: ...
- 机器学习之k-最近邻(kNN)算法
一.kNN(k-nearest neighbor)算法原理 事物都遵循物以类聚的思想,即有相同特性的事物在特征空间分布上会靠得更近,所以kNN的思路是:一个样本在特征空间中k个靠的最近的样本中,大多数 ...
- Algorithm - 贪心算法使用场景 ( LEETCODE —— Best Time to Buy and Sell Stock II)
先看一道leetcode题: Best Time to Buy and Sell Stock II Say you have an array for which the ith element is ...
- JS特效@缓动框架封装及应用
| 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.变量CSS样式属性获取/赋值方法 给属性赋值:(既能获取又能赋值) 1)div.style.width 单个赋值:点语法,这个方法比较固定 ...
- 北航MOOC系统Android客户端NABC
北航MOOC手机客户端NABC分析 1) N (Need 需求) MOOC是Massive Open Online Course的缩写,通常被译为大型开放式网络课程,它最早在08年的时候由一位加拿大的 ...
- 2-Sixth Scrum Meeting20151206
任务分配 闫昊: 今日完成:请假.(最近代码写得多……很累……) 明日任务:完成数据库设计. 唐彬: 今日完成:ios客户端代码的深度学习. 明日任务:读IOS讨论区后台接口. 史烨轩: 今日完成:请 ...
- ASP.net四则运算《《《策略模式
Calculator.cs using System; using System.Collections.Generic; using System.Linq; using System.Web; / ...
- 《TCP/IP 详解 卷1:协议》第 2 章:Internet 地址结构
第二章介绍 Internet 使用的网络层地址,即熟知的 IP 地址.连接到 Internet 的设备,基于 TCP/IP 的专用网络中使用的设备都需要一个 IP 地址. 路由器(见 IP 协议 一章 ...