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. poj 2451 Uyuw's Concert (半平面交)

    2451 -- Uyuw's Concert 继续半平面交,这还是简单的半平面交求面积,不过输入用cin超时了一次. 代码如下: #include <cstdio> #include &l ...

  2. supersockets服务器配置热更新

    Keywords: 配置,热更新 此功能能够允许你在不重启服务器的前提下更新服务器实例的配置. (仅限1.6.5及其以上版本)

  3. 【DCN】端口与地址绑定技术

    端口与地址绑定技术   与AM技术不同之处在于,AM端口下绑定的MAC或IP能够通信,不限制绑定的MAC在其它接口下通信.   开启MAC-CPU学习模式 mac-address-learning c ...

  4. HTML5 meta 属性整理

    <!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 --> <html lang="zh-cmn-Hans"&g ...

  5. Bi-LSTM-CRF for Sequence Labeling

    做了一段时间的Sequence Labeling的工作,发现在NER任务上面,很多论文都采用LSTM-CRFs的结构.CRF在最后一层应用进来可以考虑到概率最大的最优label路径,可以提高指标. 一 ...

  6. Codeforces Round #178 (Div. 2)

    A. Shaass and Oskols 模拟. B. Shaass and Bookshelf 二分厚度. 对于厚度相同的书本,宽度竖着放显然更优. 宽度只有两种,所以枚举其中一种的个数,另一种的个 ...

  7. 关于IFRAME的onload事件

    昨天遇到一个关于iframe的问题,比如a页面中嵌入了一个iframe称为a_iframe,如果直接在a_iframe的标签上直接加入属性的设置,onload=’’,这样才onload事件才是起作用的 ...

  8. linux scull 的内存使用

    在介绍读写操作前, 我们最好看看如何以及为什么 scull 进行内存分配. "如何"是需要全 面理解代码, "为什么"演示了驱动编写者需要做的选择, 尽管 sc ...

  9. dotnet core 通过 frp 发布自己的网站

    很多时候写出来的网站只能自己内网访问,本文告诉大家如何通过 Frp 将自己的 asp dotnet core 网站发布到外网,让小伙伴访问自己的网站 通过 frp 的方式,可以解决自己的服务器性能太差 ...

  10. H3C 递归查询