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,也就是 ...
随机推荐
- HTML中由于DIV(块元素)浮动,导致的父元素高度塌陷问题的解决方案
费话不多说,直接上问题: 1.开始时,页面只有两个DIV的嵌套(见图) 运行结果是: 现在看运行的是正常的,但是当我设置让 class="box2" 的DIV浮动时 运行结果是这样 ...
- vue打包后找不到资源路径问题
问题描述: 使用webpack打包vue项目后,前后端联调无法找到资源 解决方案: 一. 改为相对路径,去除axios中地址的第一个“/” onProxyReq: function (proxyReq ...
- Oracle 如何查看当前的实例及切换实例
一.Oracle查看当前实例 1.打开终端,输入命令sqlpuls / as sysdba连接到数据库 2.输入命令show parameter name便可以查看当前登录数据库的参数配置,如下可以看 ...
- spring 整合mongodb报NoSuchMethodError错误
刚开始通过网上查到相关的资料进行了一些配置,参考链接:http://www.open-open.com/lib/view/open1454374782167.html maven的dependenci ...
- Django学习:连接Mysql数据库
开发环境: Windows 10 Python 3.7.4 Django 2.2.6 Mysql 8.0.17 承接上一节:https://www.cnblogs.com/daydayupup/p/1 ...
- Django—处理流程
用户通过浏览器发送请求 请求到达request中间件,中间件对request请求做预处理或者直接返回response 若未返回response,会到达urlconf路由,找到对应视图函数 视图函数做相 ...
- c++实现单向链表的一些操作
#include<iostream> #include <algorithm> #include <vector> #include <set> #in ...
- Ubuntu系统--安装官方flash插件包的方法
浏览器安装官方install_flash_player_npapi_linux.x86_64.tar.gz插件包的方法 第一步:下载安装包. adobe flash player的官方下载instal ...
- Java工程师面试题集锦
即将踏上找工作的征途,参考网上面试题库准备一波面试题,希望能找到理想中的工作,愿一切顺利. 一.Java基础 1.String类为什么是final的. 2.HashMap的源码,实现原理,底层结构. ...
- PostScript
https://baike.baidu.com/item/PostScript/2192822?fr=aladdin PostScript是一种编程语言,最适用于列印图像和文字(无论是在纸.胶片或非物 ...