多用户商城网站开发,国内公司网站模板,网页设计图片界面,html中秋节网页制作代码Selector#xff08;选择器#xff09;是 Channel 的多路复用器#xff0c;它可以同时监控多个 Channel 的 IO 状况#xff0c;允许单个线程来操作多个 Channel。Channel在从Buffer中获取数据。
选择器、通道、缓冲池是NIO的核心组件。
一、新建选择器 此时选择器内只包含…Selector选择器是 Channel 的多路复用器它可以同时监控多个 Channel 的 IO 状况允许单个线程来操作多个 Channel。Channel在从Buffer中获取数据。
选择器、通道、缓冲池是NIO的核心组件。
一、新建选择器 此时选择器内只包含这一条负责监听连接请求的通道
二、减少阻塞之事件触发送到嘴边
一、不用阻塞等待建立连接
答最最关键的一步是选择器的存在同时下图第一个红框ServerSocketChannel属性设置为非阻塞也有一定作用 选择器监听通道所监视的正是通道中的事件key就代表通道中出现的事件在循环开始后选择器调用select() 方法监控选择器中通道状态 有 3 种方式可以 select 就绪事件 1select() 阻塞方法只要出现一个就绪事件就会返回。没有则一直保持阻塞状态。 2select(long timeout) 阻塞方法有一个就绪事件或者其它线程调用了 wakeup()或者当前线程被中断或者阻塞时长达到了 timeout 时返回。不抛出超时异常。 3selectNode() 不阻塞如果无就绪事件则返回 0如果有就绪事件则将就绪事件放到一个集合返回就绪事件的数量。 那么select方法实现了什么 这个方法实现了选择器中的通道只有 出现一批就绪事件才会主动去处理若没有就绪事件就等着啥也不干。
这个是NIO选择器的优势来活了才干不来活就等着没活干了也不占坑死等存在就绪事件的通道才会占用资源减少功耗
当第一次开始循环选择器中只有一个ServerSocketChannel通道也就是说这时只能进行客户端到服务端的连接上图中红框key.channel()就是获得当前事件所在的ServerSocketChannel
调用accept()方法就相当于如果没有连接阻塞成功等来了连接请求后进行三次握手建立连接所以这就是
NIO优势通过选择器可以无需阻塞等待请求到来因为只有选择器检测到了通道中出现连接请求(ServerSocketChannel)或者传输数据(SocketChannel)才会使用通道进行建立连接(ssChannel1.accept())或者读取数据(sChannel.read(buffer))无需等待无需等待
二、不用阻塞等待数据
上边这个是优势中的无需等待建立连接那么无需等待请求数据在哪实现的呢
答根据ServerSocketChannel建立ServerSocket后将属性设置为非阻塞 答案在上图 根据ServerSocket通道中的连接请求建立出的新连接SocketChannel属性Blocking设为False, 表明这是一个无需等待的非阻塞数据传输通道
我们后续使用的所有数据传输通道SocketChannel 都是基于这行代码创建出来的。
优势在哪呢 就是如果是阻塞通道那么假设已经开始读数据如果一天之后才发数据下面这条语句就要等待一天直到获取完全部数据。
而因为是非阻塞所以要是没数据了直接断开就是。 当然这一切都要在最外围的死循环中执行。 三、哪里不能减少阻塞 图中有三个地方实际可以归结于两个地方。
Selector 作为多路复用 I/O 模型的核心组件能够同时监控多路 I/O 通道。选择器在 select()方法等待就绪事件地时候会阻塞在处理 I/O 事件的时候也会阻塞它的优势在于在阻塞的时候可以等待多路 I/O 就绪是一种异步阻塞 I/O 模型。与多线程处理多路 I/O 相比多路复用模型只需要单个线程即可处理万级连接没有线程切换的开销。
四、用图直观描述
**************************这个图服务端最上边前四个应该是ServerSocket****************************
最开始的时候服务端选择器开始监听(监听各通道中是否有就绪事件)目前只有一个ServerSocketChannel通道这个通道也在监听(监听连接请求), 这个通道就在listen这个地方一直等着。
之后客户端根据IP 端口像服务端发送连接请求嗯服务端的通道获得了这个就绪事件(accept事件)选择器也轮询查到了直接开始处理这个通道也就是处理这个就绪事件——执行accept()方法要经过三次握手建立TCP连接。
accept()方法建立完成之后要返回一个SocketChannel通道也就是从RecV()开始就是SocketChannel再执行了。
这两个SocketChannel就像TCP连接的两个抽象端口中间有一条看不见的线我们用这两个套接字通道就可以当成TCP连接对外提供的API,直接用就好毕竟TCP很复杂官方提供了一个封装好的API。 1SelectionKey.OP_ACCEPT 表示 accept 事件就绪。例如对于 ServerSocketChannel 来说该事件就绪表示可以调用 accept() 方法来获得与客户端连接的通道 SocketChannel。 2SelectionKey.OP_CONNECT 表示客户端与服务端连接成功。 3SelectionKey.OP_READ 表示通道中已经有了可读数据可以调用 read() 方法从通道中读取数据。 4SelectionKey.OP_WRITE 表示写事件就绪可以调用 write() 方法往通道中写入数据。 五、大佬文章
Java NIO - 基础详解 | Java 全栈知识体系
https://www.cnblogs.com/robothy/p/14242971.html
【死磕NIO】— 探索 SocketChannel 的核心原理-CSDN博客
Java NIO 中的 Channel 详解 - 掘金
Java NIO浅析 - 美团技术团队
六、完整实例代码
NIO服务端
public class NIOServer {public static void main(String[] args) throws IOException {Selector selector Selector.open();ServerSocketChannel ssChannel ServerSocketChannel.open();ssChannel.configureBlocking(false);ssChannel.register(selector, SelectionKey.OP_ACCEPT);ServerSocket serverSocket ssChannel.socket();InetSocketAddress address new InetSocketAddress(127.0.0.1, 8888);serverSocket.bind(address);while (true) {selector.select();SetSelectionKey keys selector.selectedKeys();IteratorSelectionKey keyIterator keys.iterator();while (keyIterator.hasNext()) {SelectionKey key keyIterator.next();if (key.isAcceptable()) {ServerSocketChannel ssChannel1 (ServerSocketChannel) key.channel();// 服务器会为每个新连接创建一个 SocketChannelSocketChannel sChannel ssChannel1.accept();sChannel.configureBlocking(false);// 这个新连接主要用于从客户端读取数据sChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel sChannel (SocketChannel) key.channel();System.out.println(readDataFromSocketChannel(sChannel));sChannel.close();}keyIterator.remove();}}}private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {ByteBuffer buffer ByteBuffer.allocate(1024);StringBuilder data new StringBuilder();while (true) {buffer.clear();int n sChannel.read(buffer);if (n -1) {break;}buffer.flip();int limit buffer.limit();char[] dst new char[limit];for (int i 0; i limit; i) {dst[i] (char) buffer.get(i);}data.append(dst);buffer.clear();}return data.toString();}
}
NIO客户端
public class NIOClient {public static void main(String[] args) throws IOException {Socket socket new Socket(127.0.0.1, 8888);OutputStream out socket.getOutputStream();String s hello world;out.write(s.getBytes());out.close();}
}