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. 洛谷P4018 Roy&October之取石子 题解 博弈论

    题目链接:https://www.luogu.org/problem/P4018 首先碰到这道题目还是没有思路,于是寻思还是枚举找一找规律. 然后写了一下代码: #include <bits/s ...

  2. Error: Cannot find module 'webpack/bin/config-yargs' 报错原因, webpack@4.X踩的坑~

    1 .使用webpack@4.32.2时, 当我通过package.json的script去执行webpack-dev-server时, 报以下错误:  Error: Cannot find modu ...

  3. npx cowsay 让动物说话~

      发现个好玩的东东, 忍不住想分享出来, 好可爱, 哈哈哈~~   node环境执行命令:    npm i cowsay -D npx cowsay hello! npx cowsay -f sh ...

  4. [C++] 自动关闭右下角弹窗

    最近腾讯.迅雷等各种客户端,都越发喜欢在屏幕的右下角弹框了. 有骨气的人当然可以把这些软件卸载了事,但是这些客户端在某些情况下却又还是有用的.怎么办呢? 作为码农,自己实现一个自动关闭右下角弹窗的程序 ...

  5. python模块之模块导入

    模块的导入 """ 模块的导入使用:模块导入一般都要放在代码的最上面 不同模块的导入顺序: 1 内置模块 2 扩展模块 3 自定义模块 """ ...

  6. angular 点击事件阻止冒泡及默认行为

    经常遇到场景:多层级元素绑定ng-click 事件,则底层元素的点击事件存在冒泡现象,怎么解决? 类似原生JS ,只是语法稍有不同,如下: 阻止冒泡 $event.stopPropagation() ...

  7. HDU 3974 Assign the task

    Assign the task Problem Description There is a company that has N employees(numbered from 1 to N),ev ...

  8. webpack打包前删除之前的所有文件

    安装插件: npm install --save-dev clean-webpack-plugin 在webpack.prod.conf.js 中引入:  const { CleanWebpackPl ...

  9. 快排java代码

    定一个基准位,递归左右两边排序. public void fun(){ int arr[] = {2,3,4,5,6,7,822,3,4,5,8,6,5,4,2,1}; //System.out.pr ...

  10. UI 素材相关收集

    图标素材收藏网址  阿里:iconfont.cn 图片压缩:https://tinypng.com/ gif动图压缩:https://tool.gifhome.com