深入理解NIO - Selector、ServerSocketChannel、SocketChannel底层原理
IO多路复用模型
IO多路复用需要OS的支持,IO多路复用模型中,引入了一种新的系统调用,查询IO的就绪状态。
在Linux系统中,JavaNIO的Selector#select()
方法对应的系统调用为select/epoll系统调用。
通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。
目前支持IO多路复用的系统调用,有select、epoll等等。select系统调用,几乎在所有的操作系统上都有支持,具有良好的跨平台特性。epoll是在Linux 2.6内核中提出的,是select系统调用的Linux增强版本。
在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断地轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。
IO多路复用模型的特点:
IO多路复用模型的IO涉及两种系统调用(System Call),一种是IO操作,另一种是select/epoll(就绪查询)。
IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。
多路复用IO也需要轮询。负责select/epoll状态查询调用的线程,需要不断地进行select/epoll轮询,查找出达到IO操作就绪的socket连接。IO多路复用模型与同步非阻塞IO模型是有密切关系的。
对于注册在选择器上的每一个可以查询的socket连接,一般都设置成为同步非阻塞模型。仅是这一点,对于用户程序而言是无感知的。
IO多路复用模型的优点:
与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。Java语言的NIO(New IO)技术,使用的就是IO多路复用模型。在Linux系统上,使用的是epoll系统调用。
IO多路复用模型的缺点:
本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个读写过程是阻塞的。
⭐️注意:轮询的效率比较低,在硬件和操作系统层有个概念叫做“中断”, 这种机制可以在设备准备好的情况下,主动通知等待的程序。
Selector
Selector selector = Selector.open();
调用栈如下,在我的笔记本上open()
方法就是返回一个WindowsSelectorImpl
实例,在其构造方法中调用了一些native
方法。
<init>:126, WindowsSelectorImpl (sun.nio.ch)
openSelector:44, WindowsSelectorProvider (sun.nio.ch)
open:227, Selector (java.nio.channels)
startServer:63, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
ServerSocketChannel
// 2、获取通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 3.设置为非阻塞serverSocketChannel.configureBlocking(false);// 4、绑定连接ServerSocket serverSocket = serverSocketChannel.socket();InetSocketAddress address= new InetSocketAddress(NioDemoConfig.SOCKET_SERVER_PORT);serverSocket.bind(address);// 5、将通道注册到选择器上,并注册的IO事件为:“接收新连接”Print.tcfo("serverSocketChannel is linstening...");serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
ServerSocketChannel.open()
创建一个ServerSocketChannel
对象(实际类型是 ServerSocketChannelImpl)对象,如下图
# new ServerSocketChannelImpl(SelectorProviderImpl)
openServerSocketChannel:56, SelectorProviderImpl (sun.nio.ch)
open:108, ServerSocketChannel (java.nio.channels)
run:120, PipeImpl$Initializer$LoopbackConnector (sun.nio.ch)
run:76, PipeImpl$Initializer (sun.nio.ch)
run:61, PipeImpl$Initializer (sun.nio.ch)
doPrivileged:-1, AccessController (java.security)
<init>:171, PipeImpl (sun.nio.ch)
openPipe:50, SelectorProviderImpl (sun.nio.ch)
open:155, Pipe (java.nio.channels)
<init>:127, WindowsSelectorImpl (sun.nio.ch)
openSelector:44, WindowsSelectorProvider (sun.nio.ch)
open:227, Selector (java.nio.channels)
startServer:63, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
serverSocketChannel.socket()
方法,在ServerSocketChannel
中创建一个SocketChannel
对象 (实际类型是ServerSocketAdaptor)
socket:110, ServerSocketChannelImpl (sun.nio.ch)
createServerSocketChannel:110, NioReceiveServer (com.yh.stu.nio.socket)
startServer:65, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
serverSocket.bind(address)
方法最终调用的是 ServerSocketChannel
的 bind(..)
方法,调用栈和示例图如下:
bind:220, ServerSocketChannelImpl (sun.nio.ch)
bind:74, ServerSocketAdaptor (sun.nio.ch)
bind:67, ServerSocketAdaptor (sun.nio.ch)
createServerSocketChannel:113, NioReceiveServer (com.yh.stu.nio.socket)
startServer:65, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
| ||
|–|
调用 WindowsSelectorImpl
父类 SelectorImpl
的 register
方法将进行注册,源码如下:
//sun.nio.ch.SelectorImpl#registerprotected final SelectionKey register(AbstractSelectableChannel ch,int ops,Object attachment){if (!(ch instanceof SelChImpl))throw new IllegalSelectorException();//将`Selector` 和`ServerSocketChannel` 包装成了 `SelectionKeyImpl `对象SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);k.attach(attachment);synchronized (publicKeys) {implRegister(k);}k.interestOps(ops);return k;}// sun.nio.ch.WindowsSelectorImpl#implRegisterprotected void implRegister(SelectionKeyImpl ski) {synchronized (closeLock) {if (pollWrapper == null)throw new ClosedSelectorException();growIfNeeded();channelArray[totalChannels] = ski;ski.setIndex(totalChannels);fdMap.put(ski);keys.add(ski);// 将 `SelectionKeyImpl ski` 放入`HashSet` 中pollWrapper.addEntry(totalChannels, ski);totalChannels++;}}
从源码中不难看出,将Selector
和ServerSocketChannel
包装成了 SelectionKeyImpl
对象,并将 SelectionKeyImpl
放入Set
中,同时SelectionKeyImpl
下的属性interestOps
设置成 SelectionKey.OP_ACCEPT
|
其实在ServerSocketChannel
中也有一个数组,用来放 SelectionKey
的引用,方法调用栈和图示如下
addKey:115, AbstractSelectableChannel (java.nio.channels.spi)
register:213, AbstractSelectableChannel (java.nio.channels.spi)
register:280, SelectableChannel (java.nio.channels)
startServer:66, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
···
到这里一切准备就绪了,等待客户端来连接
创建一个连接
调用栈如下
<init>:129, SocketChannelImpl (sun.nio.ch)
accept:266, ServerSocketChannelImpl (sun.nio.ch)
createSocketChannel:97, NioReceiveServer (com.yh.stu.nio.socket)
startServer:78, NioReceiveServer (com.yh.stu.nio.socket)
main:177, NioReceiveServer (com.yh.stu.nio.socket)
SocketChannelImpl(SelectorProvider sp,FileDescriptor fd, InetSocketAddress remote)throws IOException{super(sp);this.fd = fd;this.fdVal = IOUtil.fdVal(fd);this.state = ST_CONNECTED;this.localAddress = Net.localAddress(fd);this.remoteAddress = remote;}
深入理解NIO - Selector、ServerSocketChannel、SocketChannel底层原理相关推荐
- Java NIO Selector , SelectionKey , SocketChannel , ServerSocketChannel
一 NIO介绍 1. NIO是非阻塞的 NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,rea ...
- 最容易理解的反射机制的底层原理
看了好多关于Java反射机制的文章,大多都太过官方,消化起来比较稍显费劲,本篇,我会依据自己的理解去阐述什么是Java的反射机制,反射用在什么地方,以及怎么来使用? 开篇前,我们还是要了解一下,什么是 ...
- 深入理解 Spring Cloud 核心组件与底层原理
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:邋遢的流浪剑客 blog.csdn.net/qq_40378 ...
- 深入理解 Spring Cloud 核心组件与底层原理!
一.Spring Cloud核心组件:Eureka Netflix Eureka Eureka详解 1.服务提供者 2.服务消费者 3.服务注册中心 二.Spring Cloud核心组件:Ribbon ...
- springcloud 组件_深入理解 Spring Cloud 核心组件与底层原理
新人大礼包,30G Java架构资料,免费领取zhuanlan.zhihu.com 一.Spring Cloud核心组件:Eureka Netflix Eureka Eureka详解 1.服务提供者 ...
- 完全理解NIO Selector
一.Selector是什么 Selector是一个或多个SelectableChannel对象的多路复用器. 二.如何创建一个Selector对象 一个selector对象可以通过调用Selector ...
- 深入理解滤波器!降噪的底层原理!滤波器到底是什么?
高通滤波器电路图如下: 我们知道,交流电频率越高越容易通过电容.高通滤波器中,电容可以让高频信号通过,把低频号拒之门外. 滤波器截止频率计算公式为. 举例:当选择电阻为1000欧,电容为160nf,此 ...
- java,从入土到出棺——2.数据结构(从容器(集合等)到底层原理)
java,从入土到出棺--2.数据结构(从容器(集合等)到底层原理) 1 数据结构 1.1 概述 1.2 数据结构的种类 1.2.1 数组(Array) 1.2.2 栈(Stack) 1.2.3 队列 ...
- 【死磕NIO】— 探索 SocketChannel 的核心原理
Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...
最新文章
- 百度地图轨迹回放,自定义路书,边走边画线
- 关于 TApplication 详解 三 ---- TComponent
- signature=0a26d8967069103efeee67346aac0529,Construction of Thinned Gated Single-Assignment Form
- Navicat连接MySQL8.0出现乱码的解决方案
- Vue.js-Day04-PM【axios(安装、使用)】
- Kettle使用_21 分组与分析窗口函数
- 从框架源码中学习结构型设计模式
- 阿里研究院:2020阿里农产品电商报告
- 如何快速从基因组中提取基因、转录本、蛋白、启动子、非编码序列?
- PLSA隐变量主题模型的公式推导解惑
- 绘图QPainter-字体
- 抹机王怎么一键新机_抹机王助手_手机数据修改_抹机王app下载_易玩网
- python判断ip是否可以ping通
- java虎牙app弹幕_虎牙直播随机弹幕插件(OBSS)
- 嵌入式分享合集106
- 软件工程网络工程第二次训练(AC代码和详细解释)(C语言描述)
- 《数学史概论》读后感
- 短信导出工具V1.3
- three.js中jsm文件夹的使用
- 【Unet系列】(三)Unet++网络
热门文章
- Javascript模块化编程系列二: 模块化的标准化(CommonJS AMD)
- 客户和顾客是一个意思吗_履约保证金和投标保证金是一个意思吗?
- java double的加法_java Double 进行加减乘除
- linux查看系统后台,求助,如何查看后台服务
- Keil5的仿真调试
- Python的第三方库xlwt
- orange实现逻辑回归_逻辑回归模型
- python超级关系_不可阻挡的超级语言--python
- html表头跟随滚动,JS实现table表格固定表头且表头随横向滚动而滚动_心善_前端开发者...
- linux下vi编辑器的命令大全,linux下VI编辑器命令大全(超级完整版)