往期文章简单讲解了Netty入门基础篇的相关基本概念:
网络编程之IO:说到IO不得不会想到NIO和BIO,说到这两个概念我们不得不看一下两个:
stream和channel
stream就是咱们以前接触的文件输入输出流,Socket输入输出流
通过前几期的文章大家对channel也已经有所了解了。简单总结一下两者的区别:
- stream不会自动缓冲数据,channel会利用系统提供的发送缓冲区,接受缓冲区(更为底层)
- stream仅支持
阻塞API,channel同时支持阻塞、非阻塞API,并且 网络channel可以配合selector实现多路复用 - 两者均为全双工,即
读写可以同时进行
⚠️注意:
这里有一个误解,stream是单向的,channel是双向的。 所以stream的读写可能会相互影响,这是不对的。 在网络编程里无论是stream还是channel都是全双工的。
接下来给大家出一个问题,相信大家有的指定遇到过。 请问 同步阻塞、同步非阻塞、同步多路复用、异步阻塞(没有这种情况)、异步非阻塞这几个概念都是什么?相信大家一看到这几个词,不由得头一大,脑袋一懵,这是什么鬼。 首先想要了解这些概念性质的东西,需要从IO模型上进行出发。
IO模型
IO模型一共有5种:
- 阻塞IO 2. 非阻塞IO 3. 多路复用 4. 信号驱动 5. 异步IO
这里参考书籍
Unix网络编程 -卷1
- 同步:线程自己去获取结果(一个线程)
- 异步:线程自己不去获取结果,而是由其它线程送结果(至少两个线程)
讲解概念之前先给大家看一张图
该图主要包含两个部分
- 1.用户程度空间(相当于Java代码) 2. Linux内核空间
- 该图主要说的就是读取数据 本身Java代码是没有网络读取数据的功能的。它需要调用操作系统的内核空间去读取数据。也就是用户空间和系统内核空间的切换。 等待数据:等待client的数据发送过来 复制数据:将数据从网卡读取到内存中
当调用一次 channel.read或 stream.read 后,会切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:
- 等待数据阶段
- 复制数据阶段
- 什么是阻塞IO(
同步的)
就是用户在读取数据期间什么也做不了,这就是
阻塞IO
- 什么是非阻塞IO(
同步的)
用户在读取数据的时候,如果没有数据会直接立刻返回,这个期间用户线程始终在运行并没有停止 ,这种情况我们称之为
非阻塞IO;
但是当用户线程发现用数据了,它就不会立刻返回了,它需要完成第二阶段
复制数据的操作, 其实在复制数据的过程中,用户线程还是阻塞的。简单来说,就是在等待数据的过程中是非阻塞的,在复制数据的时候也是阻塞的
其实这种非阻塞IO并没有什么特别好的提升,它牵扯到了用户程序空间和Linux内核空间的多次切换。 切换的太频繁反而会影响系统的性能。
- 多路复用 (
本质上也是同步的)
这个情况和以上两种都不一样,它一上来不会去调用
read方法,而是去调用select,它会阻塞住,看有没有事件,一旦有事件,内核会告诉它有事件发生了,然后用户就可以根据selectorkey去拿到channel,然后调用read,在read期间,一旦复制数据,它还是会阻塞。
-
相信大家不难发现这个多路复用也是两次阻塞,这和阻塞IO有什么区别吗?
请大家查看这两张图:
简单来说一下就是阻塞IO在与channel2
accpet建立连接的时候,如果channel1再次发来数据,阻塞IO是无法接受channel发送的数据的,它必须要等与channel2的连接建立完成后才可以。
如图所示,多路复用最大的好处就是: select可以检测多个channel上的事件,select方法执行之后它就在等待这些事件的发生,无论什么事件都会出发select向下运行,如果channel1的read事件和channe2的accept事件同时发生,那么select等待结束返回多个事件。
总结一句话就是
多路复用可以一次性的把多个channel上的事件都可以进行处理
关于网络IO模型的概念暂时就说这么多,后续持续更新
** 微信搜索【码上遇见你】获取更多精彩内容**