/*阻塞 和 非阻塞 是对于 网络通信而言的*/

  /*原先IO通信在进行一些读写操作 或者 等待 客户机连接 这种,是阻塞的,必须要等到有数据被处理,当前线程才被释放*/

  /*NIO 通信 是将这个阻塞的过程 丢给了选择器,客户端和 服务器端 之间建立的通道,都会注册到 选择器上,然后用选择器 实时监控 我们这些通道上的状况*/

  /*当某一个通道上 某一个请求的事件 完全准备就绪时,那么选择器才会将 这个任务 分配到服务器上的一个 或多个线程中*/

/*阻塞 与 非阻塞*/

  传统的IO 流都是 阻塞式的。也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务

  因此,在完成网络通信进行IO操作时,由于线程会阻塞,所以 服务器必须为每个客户端提供一个独立的线程进行处理 (这也是原来使用IO通信的解决办法)

  但是,当服务器需要处理大量客户端时,性能急剧下降

Java NIO 是非阻塞式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程 可以管理 多个 输入和 输出通道。

因此,NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器的所有客户端

/*NIO通信非阻塞的原因在于 选择器 的存在,如果不使用选择器,同样会出现阻塞的现象*/

  关于NIO阻塞的演示:
  

 public class TestBlockingNIO2 {

     // 客户端
@Test
public void client() throws IOException {
//1.获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888)); //2.分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024); //3.读取本地文件,并使用SocketChannel发送到服务器
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
while(inChannel.read(buffer) != -1) {
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
} //在这里服务端不知道 客户端数据 发没发完,线程就一直处于阻塞状态
//通过shutdownOutput 来告知服务器 我不发送数据了 //之所以上一个 程序 不用 shutdown 线程也能结束,可能是因为上一个程序只需要向服务端发送数据,而不需要接收数据,能够判断出是否发送完了数据
socketChannel.shutdownOutput(); //4.接收服务端传来的反馈
int length = 0; //这里指定一下 从 buffer 读取的长度,因为在这里buffer 中还带有了图片信息
while((length = socketChannel.read(buffer)) != -1) {
buffer.flip();
System.out.println(new String(buffer.array(),0,length));
buffer.clear();
} //4.关闭通道
inChannel.close();
socketChannel.close();
} // 服务端
@Test
public void server() throws IOException {
//1.获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.绑定连接端口号
serverSocketChannel.bind(new InetSocketAddress(8888)); //3.获取客户端连接的通道
SocketChannel socketChannel = serverSocketChannel.accept(); //4.分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024); //5.接收客户端发来的数据,并保存到本地
FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
while(socketChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
} //6.发送反馈给客户端
buffer.put("服务端接收数据成功".getBytes());
buffer.flip();
socketChannel.write(buffer); //6.关闭通道
outChannel.close();
socketChannel.close();
serverSocketChannel.close(); } }

/*选择器 (Selector)*/

选择器(Selector)是 SelectableChannle 对象的多路复用器,

/*Selector 可以同时 监控多个SelectableChannel 的 IO 状况*/,也就是说,

利用 /*Selector 可使一个单独的线程管理多个 Channel */ selector 是 非阻塞的核心

/*选择器(Selector)的应用*/

  1.创建 Selector :通过调用Selector.open() 方法创建一个 Selector

  Selector selector = Selector.open();     //创建选择器

  2.向选择器注册通道:SelectableChannel.register(Selector sel,int ops)

    如:SelectionKey key = channel.register(selector,SelectionKey.OP_READ)

  当调用 register (Selector sel,int ops) 为通道 注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定

  3.可以监听的事件类型(可使用 SelectionKey 的四个常量表示):

    读:SelectionKey.OP_READ (1)

    写:SelectionKey.OP_WRITE (4)

    连接:SelectionKey.OP_CONNECT (8)

    接收:SelectionKey.OP_ACCEPT (16)

  若注册时不止监听一个事件,则可以使用 “位或” 操作符 (|)连接      :  int interestSet = SelectionKey.OP_READ|SelectionKey.OP_WRITE

  /*SocketChannel*/

    Java NIO 中的 SocketChannel 是一个连接到TCP网络套接字的通道

    操作步骤:打开SocketChannel 读写数据 关闭SocketChannel

  /*ServerSocketChannel*/

    Java NIO 中的 ServerSocketChannel 是一个 可以监听新进来的TCP连接的通道,就像标准IO 中的 ServerSocket一样

  /*DatagramChannel*/

    Java NIO 中的 DatagramChannel 是一个能收发UDP包的通道

使用选择器完成NIO的非阻塞式通信:

 /*
* 一:使用NIO完成网络通信的三个核心:
*
*1.通道(Channel) :负责连接
*
*
*2.缓冲区(Buffer) :负责数据的存取
*
*
*3.选择器(Selector):监控SelectableChannel的IO状况
*
* */
/*可以开启多个客户端,访问服务端,客户端的数据传递给服务端是非阻塞式的
* 最后的效果 类似于聊天室
* */
public class TestNonBlockingNIO {
//客户端
@Test
public void client() throws IOException {
//1.获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888)); //2.切换非阻塞模式
socketChannel.configureBlocking(false); //3.分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024); //4.发送数据到服务端
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
String str = scanner.next();
buffer.put((new Date().toString() + "\n" + str).getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
} /*buffer.put(new Date().toString().getBytes());
buffer.flip();
socketChannel.write(buffer);
buffer.clear();*/ //5.关闭通道
socketChannel.close();
} //服务端
@Test
public void server() throws IOException {
//1.获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.切换到非阻塞模式
serverSocketChannel.configureBlocking(false); //3.绑定连接端口号
serverSocketChannel.bind(new InetSocketAddress(8888)); //4.获取选择器
Selector selector = Selector.open(); //5.将通道注册选择器
//通过SelectionKey 指定 这个 选择器 对 通道的监听事件 (这里是 accept)( SelectionKey.OP_ACCEPT)
//通过选择器监听的方式,只有等 客户端 连接 准备 就绪了,才会 accept 这个连接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //6.轮询式的 获取选择器上已经 '准备就绪' 的事件
while(selector.select() > 0) { //这代表了 当前选择器 有准备就绪的 事件(第一次循环中因为这个选择器只监听了 accept,所以这个准备就绪的事件就是accept ) //7.获取当前选择器中,所有注册的 ‘选择键(已就绪的监听事件)’
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
//8.获取准备 “就绪的” 事件
SelectionKey sk = iterator.next(); //9.判断具体是什么事件准备就绪 (是否是 accept 准备就绪)
if(sk.isAcceptable()) {
//10. 如果客户端连接 准备就绪,就使用accept 来接收
SocketChannel clientChannel = serverSocketChannel.accept(); //11.切换到非阻塞模式
clientChannel.configureBlocking(false); //12.将该客户端的通道注册到选择器上(因为要发送数据都服务器端,想要非阻塞式的,就要注册选择器)
clientChannel.register(selector, SelectionKey.OP_READ);
} else if(sk.isReadable()) { //第一次循环SelectionKey 中,是没有 read 的,SelectionKey还没有更新,//再一次 轮询式的 获取选择器上已经 '准备就绪' 的事件 后,
//13.获取当前 选择器 上 “读就绪” 状态的通道 //就可以 获取当前 选择器 上 “读就绪” 状态的通道
SocketChannel socketChannel = (SocketChannel) sk.channel(); //14.读取客户端发来的数据
ByteBuffer buffer = ByteBuffer.allocate(1024); //注:这里不能写 -1,只能写 > 0,
//可能因为会客户端一直会从控制台读取数据,然后发送给服务端,所以将通道中的数据读到缓冲区中时,因为可能一直有数据进来,所以不会返回 -1,
//如果写 != -1,会一直陷在循环中 ,必须写 > 0,确定是有真实的数据过来的 while(socketChannel.read(buffer) > 0) {
buffer.flip();
System.out.println(new String(buffer.array()));
buffer.clear();
}
} //15.取消选择键 SelectionKey,不取消,他就一直有效,SelectionKey 就无法更新
iterator.remove();
}
}
}
}

4.NIO的非阻塞式网络通信的更多相关文章

  1. JAVA NIO学习三:NIO 的非阻塞式网络通信

    紧接着上一章,我们继续来研究NIO,上一章中我们讲了NIO 中最常见的操作即文件通道的操作,但实际上NIO的主要用途还是在于网络通信,那么这个时候就会涉及到选择器,这一章我们就会对其进行讲解操作. 一 ...

  2. NIO 的非阻塞式网络通信

    1.阻塞与非阻塞   ①  传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write()时, 该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务. 因 ...

  3. Java基础——NIO(二)非阻塞式网络通信与NIO2新增类库

    一.NIO非阻塞式网络通信 1.阻塞与非阻塞的概念  传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在 ...

  4. JAVA NIO学习记录2-非阻塞式网络通信

    一.阻塞与非阻塞 传统的IO 流都是阻塞式的.也就是说,当一个线程调用read() 或write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务.因此,在完成网络通信 ...

  5. Java IO(3)非阻塞式输入输出(NIO)

    在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...

  6. Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程

    Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程 缓冲区(Buffer) 用于存储数据 通道(Channel) 用于传输数据 多路复用器(Selector) 用于轮询 Channel 状 ...

  7. 基于NIO写的阻塞式和非阻塞式的客户端服务端

    由于功能太过简单,就不过多阐述了,直接上阻塞式代码: package com.lql.nio; import org.junit.Test; import java.io.IOException; i ...

  8. Linux NIO 系列(03) 非阻塞式 IO

    目录 一.非阻塞式 IO 附:非阻塞式 IO 编程 Linux NIO 系列(03) 非阻塞式 IO Netty 系列目录(https://www.cnblogs.com/binarylei/p/10 ...

  9. NIO非阻塞式编程

    /** * NIO非阻塞式编程<p> * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件. * 我们以服务端 ...

随机推荐

  1. 算法习题---4-4骰子涂色(UVa253)

    一:题目 分别对两个骰子的六个面涂色r-红 b-蓝 g-绿,通过转动骰子,看两个骰子是不是一样的涂色方法 (一)题目详解 题目规定了正方体的六个面的序号:从1-,按照这个需要提供涂色序列 (二)案例展 ...

  2. jmeter 随机取一个值的方法

    1.添加用户自定义变量 在要用到随机值的地方写入 ${__RandomFromMultipleVars(1|2|0)} 例子: 效果:

  3. Kafka Connect REST Interface

    Since Kafka Connect is intended to be run as a service, it also supports a REST API for managing con ...

  4. python面向对象之类属性,实例属性

    python中的属性分为类属性和实例属性,之前已经说过一些,这里主要是对类属性与实例属性的增删改查 首先是对类属性的增删改查,下面这个是对类属性的修改,在书写类时,已经对类属性occupation进行 ...

  5. 《Fluid Engine Development》 学习笔记1-求解线性方程组

    我个人对基于物理的动画很感兴趣,最近在尝试阅读<Fluid Engine Development>,由于内容涉及太多的数学问题,而单纯学习数学又过于枯燥,难以坚持学习(我中途放弃好多次了) ...

  6. TiKV事务实现浅析

    TiKV事务实现浅析 Percolator事务的理论基础 Percolator的来源 Percolator事务来源于Google在设计更新网页索引的系统时提出的论文Large-scale Increm ...

  7. NLog文章系列—系列文章目录以及简要介绍

    参考文章:http://www.cnblogs.com/dflying/archive/2006/12/04/581750.aspx

  8. mui 打包发布ios 测试

    1.首先在Hbuilder新建一个app项目,把你的代码放进来 2.在manifest.json里设置你想要的一切,图标,应用名,描述,入口页面等等等,然后再配置好你程序里需要用到的模块权限,按需配置 ...

  9. [转帖]「日常小记」linux中强大且常用命令:find、grep

    「日常小记」linux中强大且常用命令:find.grep https://zhuanlan.zhihu.com/p/74379265 在linux下面工作,有些命令能够大大提高效率.本文就向大家介绍 ...

  10. [转帖]微软宣布即将开始大规模推送Windows 10 V1903重大版本更新

    微软宣布即将开始大规模推送Windows 10 V1903重大版本更新 https://www.cnbeta.com/articles/tech/894303.htm 微软要批量更新 1903了 bu ...