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的更多相关文章

  1. 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输

    002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...

  2. Java NIO类库Selector机制解析(下)

    五.  迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel ...

  3. Java NIO之Selector(选择器)

    历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂re ...

  4. java NIO介绍

    前言 我们在写java程序的时候,为了进行优化,把全部的精力用在了处理效率上,但是对IO的关注却很少.这也可能是由以前java早期时JVM在解释字节码时速度慢,运行速率大大低于本地编译代码,因此以前往 ...

  5. Java NIO类库Selector机制解析(上)

    一.  前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...

  6. Java NIO 选择器(Selector)的内部实现(poll epoll)

    http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java NI ...

  7. Java NIO类库Selector机制解析--转

    一.  前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...

  8. Java NIO 选择器(Selector)的内部实现(poll epoll)(转)

    转自:http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java ...

  9. Netty快速入门(03)Java NIO 介绍-Buffer

    NIO 介绍 NIO,可以说是New IO,也可以说是non-blocking IO,具体怎么解释都可以. NIO 1是在JSR51里面定义的,在JDK1.4中引入,因为BolckingIO不支持高并 ...

随机推荐

  1. 2018-10-19-Nuget-通过-dotnet-命令行发布

    title author date CreateTime categories Nuget 通过 dotnet 命令行发布 lindexi 2018-10-19 09:15:53 +0800 2018 ...

  2. Python--day40--threading模块的几个方法

    import time import threading #threading.get_ident() 查看当前进程号 def wahaha(n): time.sleep(0.5) print(n,t ...

  3. linux zookeeper开机启动

    1.在zkEnv.sh中指定当前用户jdk环境变量 export JAVA_HOME=/usr/local/src/jdk1.7.0_55/ 2.在/etc/rc.d/init.d文件夹下创建zook ...

  4. HDU1172猜数字 [模拟]

    1.题意 任务是猜一个四位数,每次尝试后会给出这次猜中了几个数字和猜中了几个位置,求能否根据尝试的记录给出答案 2.分析 数据给出查询次数和每次查询的数及其有几个数和几个位置符合,值得注意的是,猜对的 ...

  5. Dubbo-本地Bean测试

    Dubbo本地测试API的Bean 一.建立一个测试类文件 二.测试API // 自己要测试的API public static final XxApi xxApi; 三.注入Bean static ...

  6. Python实现截图功能

    Python实现截图功能 Windows环境下需要用到PIL库,使用pip安装PIL库: pip install Pillow 安装完成,截图方法代码: from PIL import ImageGr ...

  7. Spring in action读书笔记(一) 自动化装配bean

    需要引入的ja包 <dependency> <groupId>org.springframework</groupId> <artifactId>spr ...

  8. CSP201803-4棋局评估

    问题描述 Alice和Bob正在玩井字棋游戏. 井字棋游戏的规则很简单:两人轮流往3*3的棋盘中放棋子,Alice放的是“X”,Bob放的是“O”,Alice执先.当同一种棋子占据一行.一列或一条对角 ...

  9. 【题解】HDU5845 Best Division (trie树)

    [题解]HDU5845 Best Division (trie树) 题意:给定你一个序列(三个参数来根),然后请你划分子段.在每段子段长度小于等于\(L\)且子段的异或和\(\le x\)的情况下最大 ...

  10. JSONP Hijackin攻击详解

    JSONP Hijackin的中文意思是JSON劫持,而能产生JSON数据劫持的原因在于前端被跨站攻击了.跨站=跨域,跨域从字面上理解的话,就是指超出了范围.领域.继续追问一下,那超出了什么范围?原来 ...