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文件类型分:普通文件.目录文件.链接文件.设备文件.管道文件. 1.普通文件:由ls -al显示属性时,第一个属性为 [-],例如 [-rwxrwxrwx].包括: 纯文本文件(ASCI ...

  2. 用SpringMVC实现的上传下载

    1.导入相关jar包 commons-fileupload.jar commons-io.jar 2.配置web.xml文件 <?xml version="1.0" enco ...

  3. DataGridView 动态绑定 CheckBox

    下面演示如何在 DataGridView 中动态绑定 CheckBox: public class Test { /// <summary> /// 构造器 /// </summar ...

  4. STL内存分配方式

    关于STL用的很多比如map, vector, set, queue, stack等等.很少关注它的内存分配情况,但是经常遇到比如使用map,不停的在map中插入了一些很小的对象,然后释放了一些,然后 ...

  5. 287 Find the Duplicate Number 寻找重复数

    一个长度为 n + 1 的整形数组,其中的数字都在 1 到 n 之间,包括 1 和 n ,可知至少有一个重复的数字存在.假设只有一个数字重复,找出这个重复的数字.注意:    不能更改数组内容(假设数 ...

  6. excel poi 取单元格的值

    /** * 取单元格的值 * * @param cell 单元格对象 * @param treatAsStr 为true时,当做文本来取值 (取到的是文本,不会把“1”取成“1.0”) * @retu ...

  7. [ Luogu 4917 ] 天守阁的地板

    \(\\\) \(Description\) 定义二元函数\(F(x,y)\)表示,用 \(x\times y\) 的矩形不可旋转的铺成一个任意边长的正方形,所需要的最少的矩形个数. 现在\(T\)组 ...

  8. 深度优先搜索DFS和广度优先搜索BFS简单解析

    转自:https://www.cnblogs.com/FZfangzheng/p/8529132.html 深度优先搜索DFS和广度优先搜索BFS简单解析 与树的遍历类似,图的遍历要求从某一点出发,每 ...

  9. 百度AI车牌识别测试

    测试背景 百度已发布诸多AI应用,其中包含车牌识别,免费使用量是200次/日.付费的话,按月调用次数在20万次到50万次之间,每日10000次,月费用为0.0035*300000=1050元. 详见: ...

  10. Java 基础入门随笔(11) JavaSE版——继承、覆盖、抽象类

    1.面向对象的特征二:继承 定义: 指一个对象直接使用另一对象的属性和方法. 继承好处: 1.提供代码的复用性. 2.让类与类直接产生了关系,给第三个特征多态提供了前提. java中支持单继承.不直接 ...