JAVA NIO 之 Selector 组件
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 组件的更多相关文章
- Java NIO类库Selector机制解析(下)
五. 迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel ...
- Java NIO类库Selector机制解析(上)
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- Java NIO 选择器(Selector)的内部实现(poll epoll)
http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java NI ...
- Java NIO类库Selector机制解析--转
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- Java NIO之Selector(选择器)
历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂re ...
- Java NIO 选择器(Selector)的内部实现(poll epoll)(转)
转自:http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java ...
- Netty快速入门(05)Java NIO 介绍-Selector
Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...
- Java NIO之Selector
选择器是JavaNIO重磅推出的一个概念:在旧有的系统中为了跟踪多端口消息,需要为每一个端口配备一个线程做监听:但是有了selector就不需要了,一个Selector可以管理一众渠道(channel ...
- Java NIO教程 Selector
这次我们开讲非阻塞I/O中的Selector,它需要配合非阻塞的TCP和UDP来使用.首先我们先简单讲一下TCP和UDP的非阻塞通道. 非阻塞I/O通道 在上代码前我们先讲解一些最基本的知识.TCP和 ...
随机推荐
- codechef : TREDEG , Trees and Degrees
其实有原题,生成树计数 然鹅这题里面是两道题, 50pts 可以用上面那题的做法直接过掉,另外 50pts 要推推式子,搞出 O n 的做法才行(毕竟多项式常数之大您是知道的) 虽说这道题里面是没有 ...
- SQL 经典语句大全
原地址:http://www.cnblogs.com/yubinfeng/archive/2010/11/02/1867386.html 一.基础 1.说明:创建数据库 CREATE DATABASE ...
- sql删除表中重复记录只保留一条记录
最终代码 update T_Fee set gzl_dfg_op = 'delete' where MetReadRecordID in ( select MetReadRecordID from T ...
- Android OKHttp网络框架
好久没逛简书了.这周公司的项目也已经愉快的迭代了新版本,对于之前一直存留的东西一直没怎么梳理,今天想说说这两年特别火的网络框架.okhttp我想大部分Android开发者都不陌生,因为它的到来.是我们 ...
- Django基础之创建程序
环境配置 安装python35 安装django:pip install django==1.9.5 创建django程序 1.命令行创建django程序 A django-admin startpr ...
- 使用QTP录制自带Flight小实例
1.双击打开QTP10.0,启动过程中测试类型选择“WEB”. 2.进入主界面,New——Test,新建一个测试用例. 3.点击Record按钮,Record and settings对话框中,可以选 ...
- mybatis 关联查询
1.关联的两个实体类 外部类 parent public class Parent{ private String parentId; private String parentName; priva ...
- 对Oracle 、SQL Server、MySQL、PostgreSQL数据库优缺点分析
对Oracle .SQL Server.MySQL.PostgreSQL数据库优缺点分析 Oracle Database Oracle Database,又名Oracle RDBMS,或简称Oracl ...
- Vue.js——router-link阻止click事件
router-link 只能单纯做路由跳转 https://segmentfault.com/q/1010000007896386
- interface与抽象类
抽象类: 定义:在 class 前加了 abstract 关键字且存在抽象方法(在类方法 function 关键字前加了 abstract 关键字)的类 抽象类不能被实例化. 抽象类被继承之后,子类必 ...