Java-NIO 之 Selector 与 Pipe
关于阻塞与非阻塞:https://www.cnblogs.com/jhxxb/p/11272727.html
一、传统的 IO 流都是阻塞式的
当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。
因此,在网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。
package nio; import org.junit.Test; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Scanner; public class TestBlockingNIO { // 客户端
@Test
public void client() throws IOException {
// 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); // 分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); // 发送到服务端
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
String str = scan.next();
buf.put(str.getBytes());
buf.flip();
sChannel.write(buf);
buf.clear(); if ("exit".equals(str)) {
break;
}
} // 接收服务端的反馈
sChannel.shutdownOutput();
int len = 0;
while ((len = sChannel.read(buf)) != -1) {
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
} // 关闭通道
sChannel.close();
} // 服务端
@Test
public void server() throws IOException {
// 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open(); // 绑定连接
ssChannel.bind(new InetSocketAddress(9898)); retry:
while (true) {
// 获取客户端连接的通道
SocketChannel sChannel = ssChannel.accept(); // 分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); // 接收客户端的数据
while (sChannel.read(buf) != -1) {
String str = new String(buf.array()).trim();
if ("exit".equals(str)) {
break retry;
}
buf.flip();
System.out.println(str);
buf.clear();
} // 发送反馈给客户端
buf.put("服务端接收数据成功!".getBytes());
buf.flip();
sChannel.write(buf); // 关闭通道
sChannel.close();
} // 关闭通道
ssChannel.close();
}
}
二、Java NIO 是非阻塞式的
当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。
线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。
因此,NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。
注:NIO 的 IO 行为还是同步的。
/*
* 使用 NIO 完成网络通信的三个核心:
*
* 1. 通道(Channel):负责连接
* java.nio.channels.Channel 接口:
* |--SelectableChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
*
* 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
* 可以监听的事件类型(可使用 SelectionKey 的四个常量表示)
* 读: SelectionKey.OP_READ (1)
* 写: SelectionKey.OP_WRITE (4)
* 连接: SelectionKey.OP_CONNECT(8)
* 接收: SelectionKey.OP_ACCEPT (16)
*
* Selector 常用方法
* Set<SelectionKey> keys():所有的 SelectionKey 集合。代表注册在该 Selector上的 Channel
* selectedKeys():被选择的 SelectionKey 集合。返回此Selector的已选择键集
* intselect():监控所有注册的 Channel,当它们中间有需要处理的 IO 操作时,该方法返回,并将对应得的 SelectionKey 加入被选择的 SelectionKey 集合中,该方法返回这些 Channel 的数量。
* int select(long timeout):可以设置超时时长的 select() 操作
* int selectNow():执行一个立即返回的 select() 操作,该方法不会阻塞线程
* Selector wakeup():使一个还未返回的 select() 方法立即返回
* void close():关闭该选择器
*/
1.TCP-SocketChannel
package nio; import org.junit.Test; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner; public class TestNonBlockingNIO { //客户端
@Test
public void client() throws IOException {
// 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); // 切换非阻塞模式
sChannel.configureBlocking(false); // 分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); // 发送数据给服务端
Scanner scan = new Scanner(System.in); while (scan.hasNext()) {
String str = scan.next();
buf.put((new Date().toString() + "\n" + str).getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
} // 关闭通道
sChannel.close();
} //服务端
@Test
public void server() throws IOException {
// 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open(); // 切换非阻塞模式
ssChannel.configureBlocking(false); // 绑定连接
ssChannel.bind(new InetSocketAddress(9898)); // 获取选择器
Selector selector = Selector.open(); // 将通道注册到选择器上, 并且指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ); // 轮询式的获取选择器上已经“准备就绪”的事件
while (selector.select() > 0) { // 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) {
// 获取准备“就绪”的是事件
SelectionKey sk = it.next(); // 判断具体是什么事件准备就绪
if (sk.isAcceptable()) {
// 若“接收就绪”,获取客户端连接
SocketChannel sChannel = ssChannel.accept(); // 切换非阻塞模式
sChannel.configureBlocking(false); // 将该通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ);
} else if (sk.isReadable()) {
// 获取当前选择器上“读就绪”状态的通道
SocketChannel sChannel = (SocketChannel) sk.channel(); // 读取数据
ByteBuffer buf = ByteBuffer.allocate(1024); int len = 0;
while ((len = sChannel.read(buf)) > 0) {
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
} // 移除当前 SelectionKey
it.remove();
}
}
}
}
2.UDP-DatagramChannel
package nio; import org.junit.Test; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner; public class TestNonBlockingNIO2 { @Test
public void send() throws IOException{
DatagramChannel dc = DatagramChannel.open(); dc.configureBlocking(false); ByteBuffer buf = ByteBuffer.allocate(1024); Scanner scan = new Scanner(System.in); while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + ":\n" + str).getBytes());
buf.flip();
dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
buf.clear();
} dc.close();
} @Test
public void receive() throws IOException{
DatagramChannel dc = DatagramChannel.open(); dc.configureBlocking(false); dc.bind(new InetSocketAddress(9898)); Selector selector = Selector.open(); dc.register(selector, SelectionKey.OP_READ); while(selector.select() > 0){
Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while(it.hasNext()){
SelectionKey sk = it.next(); if(sk.isReadable()){
ByteBuffer buf = ByteBuffer.allocate(1024); dc.receive(buf);
buf.flip();
System.out.println(new String(buf.array(), 0, buf.limit()));
buf.clear();
}
} it.remove();
}
}
}
三、Pipe(管道)
Java NIO 管道是 2 个线程之间的单向数据连接。Pipe 有一个 source 通道和一个 sink 通道。数据会被写到 sink 通道,从 source 通道读取。
package nio; import org.junit.Test; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe; public class TestPipe { @Test
public void test() throws IOException {
// 获取管道
Pipe pipe = Pipe.open(); ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put("通过单向管道发送数据".getBytes());
buf.flip(); // 将缓冲区中的数据写入管道
Pipe.SinkChannel sinkChannel = pipe.sink();
sinkChannel.write(buf); ByteBuffer buf2 = ByteBuffer.allocate(1024);
// 读取缓冲区中的数据
Pipe.SourceChannel sourceChannel = pipe.source();
int len = sourceChannel.read(buf2);
System.out.println(new String(buf2.array(), 0, len)); sourceChannel.close();
sinkChannel.close();
}
}
Java-NIO 之 Selector 与 Pipe的更多相关文章
- 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机制解析--转
一. 前言 自从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(选择器)
历史回顾: 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 ...
- 转:Java NIO系列教程(九) Pipe
Java NIO 管道是2个线程之间的单向数据连接.Pipe有一个source通道和一个sink通道.数据会被写到sink通道,从source通道读取. 这里是Pipe原理的图示: 创建管道 通过Pi ...
- Java NIO之Selector
选择器是JavaNIO重磅推出的一个概念:在旧有的系统中为了跟踪多端口消息,需要为每一个端口配备一个线程做监听:但是有了selector就不需要了,一个Selector可以管理一众渠道(channel ...
- Java NIO学习笔记八 Pipe
Java NIO Pipe Java NIO管道是两个线程之间的单向数据连接.Pipe 具有源信道和接受通道.您将数据写入sink通道.然后可以从源通道读取该数据. 这是一个原理的Pipe流程图: J ...
- Netty快速入门(05)Java NIO 介绍-Selector
Java NIO Selector Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读.可写.如此可以实现单线程管理多个channels,也就是 ...
随机推荐
- 二元变量图形的pandas方法
数据加载: 1.散点图 上图使用下采样的方法选取了100个样本点,因为把所有的数据加载进来太多了. 2.Hexplot图 上图是一个散点图再加上热力标注的形式,可以更准确的帮助我们看出数据集中在哪些区 ...
- STM32工程模版
STM32工程模版,看过来 ST库源码去官方下载 创建工程目录 doc:存放说明文档 lib:存放库文件 listing:存放编译产生的中间文件 output:存放生成的文件 project:存放工程 ...
- Django + mysql 在创建数据库出错
错误:django.db.utils.OperationalError: (1366, "Incorrect string value: '\\xE6\\x96\\x87\\xE7\\xAB ...
- Java和操作系统交互(Java 代码是怎么执行)(转)
结合 CPU 理解一行 Java 代码是怎么执行的 根据冯·诺依曼思想,计算机采用二进制作为数制基础,必须包含:运算器.控制器.存储设备,以及输入输出设备,如下图所示. 我们先来分析 CPU 的工作原 ...
- C#读取某一文件夹下的所有文件夹和文件
static List<string> list = new List<string>();//定义list变量,存放获取到的路径 /// <summary> // ...
- u-boot移植易用性设置
u-boot移植易用性设置 以下设置使用的u-boot版本为u-boot-2012.04.01 环境参数 在Flash上划分了一块区域用于存储环境变量,所以当u-boot启动时会有如下操作: 读取Fl ...
- 算法笔记--CDQ分治 && 整体二分
参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 前置技能:树 ...
- java -为什么重写equals(),还需要重写hashCode()?
1.先post这两个方法的基本定义: equals()的定义: 浅谈Java中的equals和==(转) hashCode()的定义: java中hashCode()方法的作用 Java中hashCo ...
- kotlin变量基础
kotlin变量与输出 这里看一下在Kotlin中的变量是如何定义的? 编译运行: 对比下我们常用的JAVA语言,定义上的区别: 1.Kotlin变量是统一以var开头,而不区分具体类型,而Java在 ...
- C语言|博客作业12-学期总结
我学到的内容 我的收获 第一次:https://www.cnblogs.com/tongyingjun/p/11563433.html:总结:初步了解了C语言.代码.博客园和Markdown语法. 第 ...