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,也就是 ...
随机推荐
- vue runtime报错问题
Webpack中导入vue和普通网页中导入vue的区别1. 普通网页导入vue方式 <script></script> 2. Webpack导入vue方式 Import Vue ...
- js-回文数
回文数 设n是一任意自然数.若将n的各位数字反向排列所得自然数n1与n相等,则称n为一回文数. //回文数 let readline = require("readline-sync&quo ...
- Spring Data JPA引入和介绍
第1章 1.ORM概述[了解] ORM(Object-Relational Mapping) 表示对象关系映射.在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中.只要有一套程序能 ...
- Delphi 对象的特性
- 基础网络之EfficientNet
摘要: 一般情况下,我们都会根据当前的硬件资源来设计相应的卷积神经网络,如果资源升级,可以将模型结构放大以获取更好精度.我们系统地研究模型缩放并验证网络深度,宽度和分辨率之间的平衡以得到更好的性能表现 ...
- 《浏览器工作原理与实践》<07>变量提升:JavaScript代码是按顺序执行的吗?
讲解完宏观视角下的浏览器后,从这篇文章开始,我们就进入下一个新的模块了,这里我会对 JavaScript 执行原理做深入介绍. 今天在该模块的第一篇文章,我们主要讲解执行上下文相关的内容.那为什么先讲 ...
- .NET SignalR中长连接与HUB连接的使用方式以及区别
1 using Microsoft.AspNet.SignalR; 2 using System; 3 using System.Collections.Generic; 4 using System ...
- u-boot移植易用性设置
u-boot移植易用性设置 以下设置使用的u-boot版本为u-boot-2012.04.01 环境参数 在Flash上划分了一块区域用于存储环境变量,所以当u-boot启动时会有如下操作: 读取Fl ...
- TCP 的三次握手和四次挥手,TCP 的流量控制和拥塞控制
70.TCP协议的三次握手与四次挥手70.1.TCP报文结构 1.源端口号:表示发送端端口号,字段长为16位. 2.目标端口号:表示接收端口号,字段长为16位. 3.序列号:表示发送数据的位置 ...
- Python&Selenium 数据驱动【unittest+ddt】
一.摘要 本博文将介绍Python和Selenium做自动化测试的时候,基于unittest框架,借助ddt实现数据驱动 二.测试代码 # encoding = utf-8 ""& ...