NIO模型(Non Blocking IO)

  • 1、什么是NIO
  • 2、NIO初始版本示例代码
    • 2.1、服务端代码
    • 2.2、客户端测试
    • 2.3、原始版本出现的问题
  • 3、NIO引入多路复用器
    • 3.1、什么是多路复用器
    • 3.2、NIO引入多路复用器
    • 3.3、NIO引入多路复用器之后的代码

1、什么是NIO

在上一篇博客我们说到了BIO,阻塞式IO模型,在连接建立请求时和接收数据时会发生阻塞,但是多线程可以解决这个问题,但随之产生的问题就是每一个请求都会对应一个线程,当请求过多时,线程也就是1:1的增加,太多了,非常消耗资源,一般服务器承受不起。

BIO模型博客链接

所以就出现了NIO,非阻塞式线程模型,在连接请求时和读取数据时,都不会阻塞也可以叫 New Blocking IO,因为它可以设置为阻塞和非阻塞两种形态。

2、NIO初始版本示例代码

2.1、服务端代码

大概解释一下下面这堆代码
1、同样的,也是先创建一个socket连接通道,然后向外提供一个9000的监听端口。
2、设置死循环和接收连接请求时不阻塞,当有连接进来时,会去建立连接并放入到一个 SocketChannel 集合中,如下所示:

List<SocketChannel> channelList = new ArrayList<>();

3、然后每次去循环遍历这个数组去读取他么所拿到的数据,

public class NioServer {// 保存客户端连接static List<SocketChannel> channelList = new ArrayList<>();public static void main(String[] args) throws IOException {// 创建NIO ServerSocketChannel,与BIO的serverSocket类似ServerSocketChannel serverSocket = ServerSocketChannel.open();// 绑定端口号 9000serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);System.out.println("服务启动成功");while (true) {// 这个 while 循环就一直在跑// 非阻塞模式 accept 方法不会阻塞,否则会阻塞// NIO的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数SocketChannel socketChannel = serverSocket.accept();// 如果有客户端进行连接if (socketChannel != null) {System.out.println("连接成功");// 设置SocketChannel为非阻塞,读取数据前不阻塞socketChannel.configureBlocking(false);// 保存客户端连接在List中,将刚刚客户端与服务器建立的通道放到这个list中channelList.add(socketChannel);}// 遍历连接进行数据读取,不管这个通道有木有数据都会遍历Iterator<SocketChannel> iterator = channelList.iterator();while (iterator.hasNext()) {SocketChannel sc = iterator.next();ByteBuffer byteBuffer = ByteBuffer.allocate(128);// 非阻塞模式read方法不会阻塞,否则会阻塞int len = sc.read(byteBuffer);// 如果有数据,把数据打印出来if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));// 如果客户端断开,把socket从集合中去掉} else if (len == -1) {iterator.remove();System.out.println("客户端断开连接");}}}}
}

2.2、客户端测试

可以仿照 BIO 那篇博客的测试方法。

2.3、原始版本出现的问题

由上面代码我们可以分析出,由于我们将所有的连接通道都放到了一个list集合里面,所以每一次在循环遍历这个list的时候,会将所有建立过连接的通道全部遍历一遍,如果说现在有10000个通道都建立的连接,但是收发数据的通道只有100个,那么剩下的9900个通道的遍历就是无效的,这样很浪费资源。

3、NIO引入多路复用器

3.1、什么是多路复用器

在 NIO 的原始版本中,出现的问题是遍历了无效的连接通道(也就是没有数据收发的通道连接),多路复用器就相当于是一个服务员,中间人,他去收集哪些通道有数据发送,然后交给主线程来遍历读取数据,这样的话就解决了无效遍历的问题。

3.2、NIO引入多路复用器

先是一个图的大概讲解,这个图有点抽象,后续有时间尽量改进

大概挑几个重点解释一下
首先多路复用器最重要的地方就是三个方法

// 创建一个多路复用器
Selector selector = Selector.open();
// 将我们创建的通道注册到多路复用器上
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
// 取出请求事件的集合列表
selector.select();

open():
open方法会linux的一个库函数 epoll_create 来创建 epoll 对象,是一个文件描述符,因为linux一切皆文件嘛,这里也可以理解为返回了一个 epoll 对象的索引。


register:
这里其实就是简单的注册,里面调用了这行代码,pollWrapper.add(fd),很明显,它是将我们客户端的请求channel放到pollWrapper集合中。


select():
这里会调用linux的两个库函数 epoll_ctl epoll_wait
epoll_ctl 是遍历我们上面那个pollWrapper集合,真正的将channel和epoll关联起来,通过操作系统的硬中断,当有客户端访问进来时,会将这个访问放到一个 rdlist 上,这个就是我们最后取出来的需要循环处理的事件。
epoll_wait 函数就是回调返回事件,如果没有就阻塞,所以这行代码会阻塞

3.3、NIO引入多路复用器之后的代码

public class NioSelectorServer {public static void main(String[] args) throws IOException {// 创建NIO ServerSocketChannelServerSocketChannel serverSocket = ServerSocketChannel.open();// 往这个channel上绑定 9000 端口,客户端就通过这个 channel 来连接服务端serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);// 打开Selector处理Channel,即创建 epoll 对象,返回了一 epoll 文件描述符// 这里调用了linux的一个库函数  epoll_create 来创建 epoll 对象// 也可以理解为返回了一个 epoll 对象的索引Selector selector = Selector.open();// 将我们创建的通道注册到 selector(多路复用器)上,并且selector对客户端accept连接操作感兴趣// pollWrapper.add(fd) 会放到这个集合中serverSocket.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务启动成功");while (true) {// 阻塞等待需要处理的事件发生// 这里可以接收很多事件// 如果是第一次循环的话,按照这个代码来说只能接收到连接事件// 如果执行了里面的 while 循环,给它注册了其他的事件,那么它就可以感知到其他的事件的发生// 这里会调用linux的两个库函数  epoll_ctl  epoll_wait// epoll_ctl 真正的将channel和epoll关联起来,通过操作系统的硬中断,当有客户端访问进来时,//           会将这个访问放到一个 rdlist 上,这个就是我们最后取出来的需要循环处理的事件// epoll_wait 函数就是回调返回事件,如果没有就阻塞,所以这行代码会阻塞selector.select();// 获取selector中注册的全部事件的 SelectionKey 实例// 拿出所有感知的事件后弄一个迭代器Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();// 遍历SelectionKey对事件进行处理while (iterator.hasNext()) {SelectionKey key = iterator.next();// 如果是OP_ACCEPT事件,则进行连接获取和事件注册if (key.isAcceptable()) {// 拿出需要连接的客户端服务ServerSocketChannel server = (ServerSocketChannel) key.channel();// 将需要连接的客户端进行连接,并返回一个唯一channel,以便后续的读写操作SocketChannel socketChannel = server.accept();// 并设置读取操作时也不阻塞socketChannel.configureBlocking(false);// 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("客户端连接成功");}// 如果是OP_READ事件,则进行读取和打印else if (key.isReadable()) {// 取出前面连接时创建的唯一的读写channelSocketChannel socketChannel = (SocketChannel) key.channel();// 再定义一个存放数据的缓冲区ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);// 从读写channel中将需要读取的数据拿出来int len = socketChannel.read(byteBuffer);// 如果有数据,把数据打印出来if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));} else if (len == -1) {// 如果客户端断开连接,关闭SocketSystem.out.println("客户端断开连接");socketChannel.close();}}//从事件集合里删除本次处理的key,防止下次select重复处理iterator.remove();}}}
}

NIO模型(Non Blocking IO)相关推荐

  1. Reactor模型和Proactor模型:同步IO与异步IO

    Table of Contents 服务端的线程模型 2种fd 3种事件 Reactor模型-同步I/O 1.单Reactor单线程模型 2.单Reactor多线程模型 3.主从Reactor多线程模 ...

  2. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  3. Tomcat网络IO NIO模型参数设定

    2019独角兽企业重金招聘Python工程师标准>>> 1 默认IO模型 1.1 配置项的解析 Tomcat 7.0.35 的配置文件是$CATALINA_HOME/conf/ser ...

  4. OSI网络模型,IO模型,BIO模型,NIO模型,AIO模型,TCP/IP协议

    文章目录 一.OSI网络模型 1.1.网络的7层架构 1.1.1.七层架构的网络图 1.1.2 七层架构的功能和作用 1.物理层 2.数据链路层 3.网络层 4.传输层 5.会话层 6.表示层 7.应 ...

  5. 关于Blocking IO, Non-Blocking IO 和 Asynchronous I/O的理解

    文章写得很详细很清楚了,对我的理解帮助很大. 转载自:http://www.cnblogs.com/whyandinside/archive/2012/03/04/2379234.html. 概括来说 ...

  6. 【Netty】NIO 简介 ( NIO 模型 | NIO 三大组件 | 选择器 Selector | 通道 Channel | 缓冲区 Buffer | NIO 组件分配 | 缓冲区示例 )

    文章目录 I . NIO 模型 II . NIO 三大组件交互流程 III . NIO 缓冲区 IV . NIO 与 BIO 对比 V . NIO 线程分配 VI . 缓冲区 ( Buffer ) 示 ...

  7. [转]关于Blocking IO, Non-Blocking IO 和 Asynchronous I/O的理解

    ---恢复内容开始--- 关于Blocking IO, Non-Blocking IO 和 Asynchronous I/O的理解 概括来说,一个IO操作可以分为两个部分:发出请求.结果完成.如果从发 ...

  8. 详解磁盘IO、网络IO、零拷贝IO、BIO、NIO、AIO、IO多路复用(select、poll、epoll)

    文章很长,但是很用心! 文章目录 1. 什么是I/O 2. 磁盘IO 3. 网络IO 4. IO中断与DMA 5. 零拷贝IO 6. BIO 7. NIO 8. IO多路复用 8.1 select 8 ...

  9. 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...

最新文章

  1. 队列(常用数据结构之一)
  2. linux命令大全rename,Linux常用命令汇总--rename
  3. 手机安装python的步骤_小白入门:Python安装的10个步骤,极其细致!!
  4. axure html 360安装扩展,win10系统360浏览器添加Axure扩展的操作方法
  5. main函数argc,argv操作
  6. 虚拟机安装windows服务出现无法打开内核设备“\\.Global\vmx86”
  7. html中只能上传文件word,HTML文件表单,接受Word文档(HTML file form, accept Word documents)...
  8. D - 最长公共子序列问题-详细过程
  9. python字典类型可迭代_核心数据类型--字典
  10. 微博ID:SuHua_MF
  11. 一个简单的json解析器
  12. matplotlib创建图的基本方法
  13. android系统下载管理器,android 系统下载管理器
  14. 关于在线答题系统设计的一些想法
  15. c++ 函数之间 传递向量_将向量传递给C ++中的函数
  16. 在使用pyplot时报错MatplotlibDeprecationWarning
  17. Nature:为什么免疫系统可产生多样性抗体和T细胞受体?
  18. Net-snmp添加子代理示例
  19. unity添加背景图片
  20. 什么浏览器好用稳定速度快?

热门文章

  1. 老王,你的wifi密码怎么改了啊
  2. C++与Qt实现MySQL数据库的增删改查
  3. python-datetime计算时间间隔
  4. (转) ios苹果开发者账号申请
  5. git rejected
  6. 无限流量服务器印设备,[转载]人人影视获得数据银行无限空间无限流量外链服务支持...
  7. 移动设备GPU架构厂商
  8. 收藏!防范零日攻击的基础措施与高级防御
  9. [基础]qml基础控件(Text)
  10. Word怎样操作在文本添加形态图标?技巧经验!Word如何进入形状标识?