Netty快速入门(05)Java NIO 介绍-Selector
Java NIO Selector
Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。

前面说过通道就是连接,比如同一时间有很多连接过来,这些连接,也就是channel都会注册到Selector中,Selector就会巡视这些channel,看看哪些可以操作(可读或者可写),并不会进行读写操作。而仅仅进行巡视,并不是很麻烦,所以即使Selector是单个线程也是很有效率的,大家不妨想象一下for循环几万次做一些简单的事,比如输出一个字符串,所花费的时间几乎很少就能明白。
Selector之所以叫多路复用,是因为Selector这个线程可以在多个线程之间检查复用,不像bio那样一个线程只负责一个连接。通道连接来了以后要自己注册到Selector中,注册完会返回一个SelectionKey,表示Selector和被注册的channel之间的关系,也是一份凭证。
Selector每次执行select就会返回就绪的通道个数,并更新所有就绪的SelectionKey的状态。下一步就可以迭代所有通道,进行处理了。

服务端代码
上面的理论说完了,下面说一下服务端代码的流程。nio的编程比较复杂和麻烦,所以代码会比bio多很多。首先初始化的时候第一步要创建Selector:
Selector selector = Selector.open();
有了Selector后,服务端要专门创建一个 ServerSocketChannel 去接收客户端的请求连接,这个channel也需要注册到Selector中:
ServerSocketChannel serverChannel = ServerSocketChannel.open(); //创建
serverChannel.configureBlocking(false); //设置为非阻塞
serverChannel.socket().bind(new InetSocketAddress(port), 1024); //绑定端口
serverChannel.register(selector, SelectionKey.OP_ACCEPT); //设置类型为接收连接的线程,并注册到Selector中
上面最后一行的SelectionKey.OP_ACCEPT表示serverChannel只对接收连接有兴趣,其它事不会做,也就是说在把通道注册到Selector中的时候,要告诉Selector通道专门负责做哪一类的事。一个通道可以做的事有四个类型:
SelectionKey.OP_CONNECT //对请求有兴趣
SelectionKey.OP_ACCEPT //对接收有兴趣
SelectionKey.OP_READ //对读操作有兴趣
SelectionKey.OP_WRITE //对写操作有兴趣
初始化服务器的代码如下:

一旦向Selector注册了一个或多个channel后,就可以调用select方法来获取channel,select方法会返回所有处于就绪状态的channel,常用select方法具体如下:
int select()
int select(long timeout)
int selectNow()
select()方法的返回值是一个int,代表有多少channel处于就绪了。也就是自上一次select后有多少channel进入就绪。
在调用select并返回了有channel就绪之后,可以通过选中的key集合来获取channel,这个操作通过调用selectedKeys()方法:
Set selectedKeys = selector.selectedKeys();
接下来看一下获取通道的代码:

上面的逻辑很简单,调用select和selectedKeys获取所有可以操作通道,然后迭代并交给handlerInput方法去处理。这就是selecto获取和r处理所有通道的循环流程。通道分为四种类型,所以通道处理的时候,可以进行四种判断:
while(keyIterator.hasNext()){
SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // 判断通道是不是一个可以接收类型的通道,用在服务端处理的时候做判断 } else if (key.isConnectable()) { // 是否是一个连接类型的通道 } else if (key.isReadable()) { // 通道可读的时候处理 } else if (key.isWritable()) { // 通道可写的时候处理 } keyIterator.remove();
}
上面是根据通道四种类型总共要做的四种判断,最后一行注意,通道处理完后,一定要进行remove,否则还会继续处理。
来看一下接收连接的请求处理:

上面的代码逻辑就是发现一个新请求后,使用SocketChannel接收,然后设置为非阻塞的,最后注册到selector中,设置类型为SelectionKey.OP_READ,最后的sk.attach(num++);表示设置连接的编号。
当连接可读的时候,处理代码如下:

这个代码虽然多,但是逻辑简单,就是读取数据打印到控制台,最后的doWrite方法就是返回信息到客户端:

这里服务端根据实际情况作了两种判断,实际开发中channel存在几种类型就要做几种判断。这里的nio服务端的程序只是一个简单的版本,很多问题比如半包都没有考虑,但是即使这样,也比bio要复杂很多。
客户端代码
客户端代码和服务端流程上差不多,

这里使用的是SocketChannel,注册的时候,客户端要连接服务端,所以类型为SelectionKey.OP_CONNECT,循环获取channel的流程和服务端一样:

看一下处理的方法:

上面判断连接成功后,会发一个消息到服务端,前面看到服务端接收到消息后会自动给客户端返回一个消息,所以客户度也要写一个读的操作:

读的逻辑也是把读到的内容写到控制台。
NIO特性
NIO使用事件驱动模型,以前一个线程只能处理一个连接,现在单个线程可以处理很多channel,相同请求数量的情况下避免线程过多的缺点。NIO是非阻塞IO,IO读写不再阻塞,而是返回0。基于block块的传输,通常比基于流的传输更高效,并且有更高级的IO函数,zero-copy零拷贝等特性,IO多路复用大大提高了java网络应用的可伸缩性和实用性。
NIO只能说在特定的情况下,比如并发量大的时候有优势,但是如果连接数只有几百,并发也不高的情况下,nio并不一定比bio好,速度也不一定比bio快。
NIO屏蔽了底层实现,不同的操作系统多路复用模型也是不同的,比如Linux的epoll,FreeBSD的Kqueue等等,NIO基于各个操作系统的IO系统实现,在不同的平台运行还是有差异存在的,而且编程很困难,容易出各种问题,有很多陷阱,熟练掌握nio需要很多经验和技术功底。
通过上面的总结也说明了我们为什么需要netty这样的框架,会把各种复杂的问题屏蔽掉,让高性能的网络编程变得简单可靠。
代码地址:https://gitee.com/blueses/netty-demo 04
本文由博客一文多发平台 OpenWrite 发布!
Netty快速入门(05)Java NIO 介绍-Selector的更多相关文章
- 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输
002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...
- Java NIO类库Selector机制解析(下)
五. 迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel ...
- Java NIO之Selector(选择器)
历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂re ...
- java NIO介绍
前言 我们在写java程序的时候,为了进行优化,把全部的精力用在了处理效率上,但是对IO的关注却很少.这也可能是由以前java早期时JVM在解释字节码时速度慢,运行速率大大低于本地编译代码,因此以前往 ...
- Java NIO类库Selector机制解析(上)
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- Java NIO 选择器(Selector)的内部实现(poll epoll)
http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java NI ...
- Java NIO类库Selector机制解析--转
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- Java NIO 选择器(Selector)的内部实现(poll epoll)(转)
转自:http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java ...
- Netty快速入门(03)Java NIO 介绍-Buffer
NIO 介绍 NIO,可以说是New IO,也可以说是non-blocking IO,具体怎么解释都可以. NIO 1是在JSR51里面定义的,在JDK1.4中引入,因为BolckingIO不支持高并 ...
随机推荐
- Keras框架下的保存模型和加载模型
在Keras框架下训练深度学习模型时,一般思路是在训练环境下训练出模型,然后拿训练好的模型(即保存模型相应信息的文件)到生产环境下去部署.在训练过程中我们可能会遇到以下情况: 需要运行很长时间的程序在 ...
- js实现开关灯游戏
需求: 点击三个按钮,页面出现不同数量的“灯” 所有的灯有相同的点击效果.点击一个灯的时候,this和他的上下左右都会变成另一种背景色. 代码在这里~~~ 文章地址 https://www.cnblo ...
- linux mysql 查看默认端口号和修改端口号
如何查看mysql 默认端口号和修改端口号 2015-03-19 17:42:18 1. 登录mysql [root@test /]# mysql -u root -p Enter password: ...
- H3C端口状态迁移
- H3C在设备上使用TFTP服务
- 【58.75%】【BZOJ 1087】[SCOI2005]互不侵犯King
Time Limit: 10 Sec Memory Limit: 162 MB Submit: 3040 Solved: 1786 [Submit][Status][Discuss] Descri ...
- Date日期时间相关
最近在封装一个关于时间函数的功能时,竟发现这些最基本的函数都有些生疏,于是进来来总结复习下,巩固自己记忆的同时,希望能帮助到需要的人 首先了解下日期对象相关的方法 var date = new Dat ...
- 为何D3D11的几个矩阵需要转置?
在学习D3D11的时候遇到一个问题,事情是这样的: D3D11引入了常量缓存(const buffer)用来实现数据的高速传输,这块儿buffer是CPU Only Write,GPU Only Re ...
- 用ubuntu里的vim搭建一个apache2+php+mysql环境一路踩的坑
先是安装apache2,这个很顺利,一个apt install apache就搞定了. (PS:查看linux是否已经安装了apache服务,可以通过执行apachectl -v,如果安装了的话会显示 ...
- iptables 基础
SNAT 和 DNAT 是 iptables 中使用 NAT 规则相关的的两个重要概念.如上图所示,如果内网主机访问外网而经过路由时,源 IP 会发生改变,这种变更行为就是 SNAT:反之,当外网的数 ...