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. Linux上安装禅道

    linux一键安装包内置了apache, php, mysql这些应用程序,只需要下载解压缩即可运行禅道. 从7.3版本开始,linux一键安装包分为32位和64位两个包,请大家根据操作系统的情况下载 ...

  2. Glide和Picassio的比较

    http://blog.csdn.net/fancylovejava/article/details/44747759 对象池: Glide原理的核心是为bitmap维护一个对象池.对象池的主要目的是 ...

  3. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛-B-Perfect Numbers(完数)

    题目描述 We consider a positive integer perfect, if and only if it is equal to the sum of its positive d ...

  4. HTML DOM getElementById() 方法

    定义和用法 getElementById() 方法可返回对拥有指定 ID 的第一个对象的引用. 语法 document.getElementById(id) 说明 HTML DOM 定义了多种查找元素 ...

  5. 304 Range Sum Query 2D - Immutable 二维区域和检索 - 不可变

    给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2). 上图子矩阵左上角 (row1, col1) = (2, 1) ,右 ...

  6. LN : leetcode 207 Course Schedule

    lc 207 Course Schedule 207 Course Schedule There are a total of n courses you have to take, labeled ...

  7. IDEA提示 found duplicate code

    原因: IntelliJ IDEA提示Found duplicated code in this file 这不是我们代码错误,而是idea提示说我们的代码有重复,在项目的其他地方有同样的代码片段 解 ...

  8. Android开发笔记(2)——ViewGroup

    笔记链接:http://www.cnblogs.com/igoslly/p/6794344.html 一.ViewGroup 1.ViewGroup的意义——整合Layout多个不同View,并对其进 ...

  9. https与http的访问,应对苹果ATS验证问题

    为应对2017年1月1日苹果ATS的问题,微信.微博等等APP要求挂载的网页必须https访问,需要添加ssl认证. 一.SSL认证 选取了阿里云提供的免费SSL,使用期限为一年.电话咨询阿里客服,免 ...

  10. 03Microsoft SQL Server 数据类型

    Microsoft SQL Server 数据类型 数据类型 Number 类型: 数据类型 描述 存储 bit  允许0,1或NULL    tinyint 允许从 0 到 255 的所有数字. 1 ...