NIO 重要功能就是实现多路复用。Selector是SelectableChannel对象的多路复用器。一些基础知识:

选择器(Selector):选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。

可选择通道(SelectableChannel):这个抽象类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的

父类。例如:ServerSocketChannel、SocketChannel。可选择通道可以被注册到选择器上。

选择键(SelectionKey):选择键封装了特定的通道与特定的选择器的注册关系。

前面的一篇文章NIO简介中介绍了传统io的同步阻塞服务器实现,现在来看看NIO多路复用服务器的实现。NIO 利用单线程轮询事件机制,定位就绪的Channel,决定执行什么,

仅仅 select()方法阶段是阻塞的。这样一个选择器避免了之前的多个客服端时切换线程的问题。下面的一张图能描述这种场景:

代码实现:

服务器server:

public class SelectSockets {

    private static int PORT_NUMBER = 9011;

    /**
* allocateDirect(1024) 此方法创建的buffer无法调用array();直接内存
*/
private ByteBuffer buffer = ByteBuffer.allocate(1024); public static void main(String[] argv) throws Exception {
new SelectSockets().go(argv);
} public void go(String[] argv) throws Exception {
System.out.println("Listening on port " + PORT_NUMBER);
// 创建ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 获得ServerSocket
ServerSocket serverSocket = serverChannel.socket();
// 创建Selector
Selector selector = Selector.open();
// 绑定
serverSocket.bind(new InetSocketAddress(PORT_NUMBER));
// false设置为非阻塞模式
serverChannel.configureBlocking(false);
// 注册通道
////ServerSocketChannel只能注册SelectionKey.OP_ACCEPT;register(Selector sel, int ops)的ops参数可以通过serverSocketChannel.validOps()获取。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
//选择器select有三种方式,这种带时间的表示,没有连接阻塞10秒后继续或者有连接进来时继续
int n = selector.select(10000);
if (n == 0) {
continue;
}
//selectedKeys()已选择的键
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
//检查是否有效
if (!key.isValid()) {
continue;
}
//accept
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
System.out.println ("Incoming connection from: "+ channel.socket().getRemoteSocketAddress( ));
registerChannel(selector, channel, SelectionKey.OP_READ);
buffer.clear();
buffer.put("你好,我是服务器!\r\n".getBytes());
buffer.flip();
channel.write(buffer);
}
//if(key.isReadable())等价于if((key.readyOps( ) & SelectionKey.OP_READ) != 0)
if (key.isReadable()) {
readHandler(key);
}
it.remove();
}
}
} /**
* 设置感兴趣的通道属性
* @param selector
* @param channel
* @param ops
* @throws Exception
*/
protected void registerChannel(Selector selector, SelectableChannel channel, int ops) throws Exception {
if (channel == null) {
return;
}
channel.configureBlocking(false);
channel.register(selector, ops);
} /**
* 处理读取数据
* @param key
* @throws Exception
*/
protected void readHandler(SelectionKey key) throws Exception {
SocketChannel socketChannel = (SocketChannel) key.channel();
int count;
StringBuilder sb = new StringBuilder();
ByteBuffer tmpByteBuffer = ByteBuffer.allocate(1024);
//读取客服端消息
while ((count = socketChannel.read(tmpByteBuffer)) > 0) {
tmpByteBuffer.flip();
sb.append(new String(tmpByteBuffer.array()));
// 这里可以回写给客服端
while (tmpByteBuffer.hasRemaining()) {
socketChannel.write(tmpByteBuffer);
}
tmpByteBuffer.clear();
}
System.out.println("客服端"+socketChannel.socket().getRemoteSocketAddress()+"说:"+sb.toString()); if (count < 0) {
// Close channel on EOF, invalidates the key
socketChannel.close();
}
} }

  客服端:

/**
* @author monkjavaer
* @date 2018/10/23 22:23
*/
public class Client { private static final int BUFFER_SIZE = 1024;
private static int PORT = 9011;
private static String[] messages =
{"今天读到一句话,觉得很好:但行好事,莫问前程。"}; public static void main(String[] args) {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
InetSocketAddress address =new InetSocketAddress(inetAddress, PORT);
SocketChannel socketChannel = SocketChannel.open(address); for (String msg: messages) {
ByteBuffer myBuffer=ByteBuffer.allocate(BUFFER_SIZE);
myBuffer.put(msg.getBytes());
myBuffer.flip();
int bytesWritten = socketChannel.write(myBuffer);
logger(String.format("Sending Message...: %s\nbytesWritten...: %d",msg, bytesWritten));
}
logger("Closing Client connection...");
socketChannel.close();
} catch (IOException e) {
logger(e.getMessage());
e.printStackTrace();
}
} public static void logger(String msg) {
System.out.println(msg);
} }

  也可以用telnet命令测试:

telnet 127.0.0.1 9011

JAVA NIO 之 Selector 组件的更多相关文章

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

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

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

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

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

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

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

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

  5. Java NIO之Selector(选择器)

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

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

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

  7. Netty快速入门(05)Java NIO 介绍-Selector

    Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...

  8. Java NIO之Selector

    选择器是JavaNIO重磅推出的一个概念:在旧有的系统中为了跟踪多端口消息,需要为每一个端口配备一个线程做监听:但是有了selector就不需要了,一个Selector可以管理一众渠道(channel ...

  9. Java NIO教程 Selector

    这次我们开讲非阻塞I/O中的Selector,它需要配合非阻塞的TCP和UDP来使用.首先我们先简单讲一下TCP和UDP的非阻塞通道. 非阻塞I/O通道 在上代码前我们先讲解一些最基本的知识.TCP和 ...

随机推荐

  1. [COCI2006-2007 Contest#3] BICIKLI

    不难的一道题,就是码的时候出了点问题,看了其他巨佬的题解才发现问题所在... 题目大意: 给定一个有向图,n个点,m条边.请问,1号点到2号点有多少条路径?如果有无限多条,输出inf,如果有限,输出答 ...

  2. linux 定义变量 ,添加变量值

    1.设置值$name=test2. 输出变量的值 echo $echo $name3. 增加变量内容PATH=$PATH:/home/bin/testPATH="$PATH":/h ...

  3. [ZJOI2005]沼泽鳄鱼

    题目描述 潘塔纳尔沼泽地号称世界上最大的一块湿地,它地位于巴西中部马托格罗索州的南部地区.每当雨季来临,这里碧波荡漾.生机盎然,引来不少游客. 为了让游玩更有情趣,人们在池塘的中央建设了几座石墩和石桥 ...

  4. exe4j将可执行的jar封装成exe文件

    1,将java项目打包成可执行的jar:https://www.cnblogs.com/3b2414/p/9355292.html, 2,下载好exe4j工具, 3,首先注册,如果你不注册,打包好的软 ...

  5. C语言指针的理解以及指针的指针的理解

    指针指向的是内存地址编号,内存地址编号指向的是对应的内容. 我们需要一个变量,来储存内存地址编号,这个变量的值是一个内存地址编号,但是我们可以通过修改变量的值,来不断的改变内存地址编号. 但是,我们如 ...

  6. struts2之通配符映射

    系统有n多个请求时候,不可能以一个action对应一个映射.可以用通配符映射将成百上千请求简化成一个通用映射. 通配符映射规则:1.若找到多个匹配,没有通配符的将胜出. 2.若指定的动作不存在,str ...

  7. StyleAI厚积薄发: Android网络图片数据传输

    在StyleAI上厚积了这么长时间,憋了这么久,本来想憋个更大的,不过还是薄发一次的好. 三.直接使用别人的工程 文章:Android学习之客户端上传图片到服务器 下载地址:https://downl ...

  8. Mysql导入导出大量数据的方法、备份恢复办法

    经常使用PHP+Mysql的朋友一般都是通过phpmyadmin来管理数据库的.日常的一些调试开发工作,使用phpmyadmin确实很方便.但是当我们需要导出几百兆甚至几个G的数据库时,phpmyad ...

  9. document.mozFullScreen

    非标准该特性是非标准的,请尽量不要在生产环境中使用它! 概述 返回一个布尔值,表明当前文档是否处于全屏模式. 语法 var isFullScreen = document.mozFullScreen ...

  10. (转)Hibernate框架基础——映射集合属性

    http://blog.csdn.net/yerenyuan_pku/article/details/52745486 集合映射 集合属性大致有两种: 单纯的集合属性,如像List.Set或数组等集合 ...