(一)、回顾一下上一篇01:
(1)、NIO的几个概念:
①、Buffer :内存块,实质就是一个数组。NIO数据读或写得中转地。
②、Channel:连接设备的通道。用于向buffer提供数据或者读取数据,异步I/O支持。
③、Selector :channel事件的监听者,他能检测到一个或多个通道,并将事件分发出去
④、SelectionKey:channel上发生的事件,包含了事件的状态信息和时间以及对应的channel。
(2)、在前面总结一中,最后的时候给出了一个完整的关于NIO操作网络套接字的例子,在这里先总结一下构建基于NIO的服务端的一般步骤:
①、构造一个Selector
②、打开一个serverSocketChannel
③、设定serverSocketChannel为非阻塞
④、绑定socketserverChannel到一个主机地址和端口
⑤、注册selector并告知感兴趣的事情
(3)、Channel的状态有四种:
①、Connectable:当一个Channel完成socket连接操作已完成或者已失败。
②、Acceptable:当一个Channel已准备好接受一个新的socket连接时,channel是Acceptale
③、Readable:当一个channel能被读时。
④、Writable:当一个Channel能被写时为可写状态。
(4)、下面是NIO中的关系图,来自于《java编程思想》
(二)、 基于多线程的NIO
总结一的例子,是基于单线程的,单线程的好处是简单,不用去考虑过于复杂的线程问题,但是仔细想一下,如果数据在网络传输的过程中发生了阻塞呢,那岂不是要花费很多的时间?再者如果我们要实现像QQ中的聊天室呢,如何实现呢?。为了解决这些问题,我们现在试着采用多线程的,但是采用多线程,会产生很多线程,创建、销毁线程都是要花费时间的,所以这里可以运用到线程池来管理。
下面一个例子是:客户端发来信息,服务端然后转发所有的信息给在线的客户端。
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.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; public class RSocketServer implements Runnable { private final static int POOLSIZE = 100;// 处理线程池的大小 private SelectionKey selectionKey; // 选择键 private ExecutorService service = Executors.newFixedThreadPool(POOLSIZE);// 固定大小的线程池 private boolean isRunning = true; private Selector selector;// 选择器 private String writeMsg;// 需要写的信息 private ServerSocketChannel ssc; public RSocketServer() { try { selector = Selector.open(); ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); ssc.socket().bind(new InetSocketAddress(8080)); selectionKey = ssc.register(selector, SelectionKey.OP_ACCEPT); System.out.println("服务器启动成功!正在端口为8080上等待..."); } catch (Exception e) { e.printStackTrace(); } } public void run() { try { while (isRunning) { int num = -1; try { // 监控注册在selector上的SelectableChannel num = selector.select(); } catch (IOException e) { e.printStackTrace(); } if (num == 0) { continue; } Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = it.next(); it.remove(); if (!key.isValid()) continue; if (key.isAcceptable()) { getConn(key); } else if (key.isReadable()) { System.out.println("可读"); readMsg(key); }else if (key.isValid() && key.isWritable()) { if (writeMsg != null) { System.out.println("可写"); RWriter(key); } } else break; } } Thread.yield(); } catch (Exception e) { e.printStackTrace(); } } private void getConn(SelectionKey key) throws IOException { ssc = (ServerSocketChannel) key.channel(); SocketChannel sc = ssc.accept(); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); } private void readMsg(SelectionKey key) throws IOException { StringBuffer sb = new StringBuffer(); SocketChannel sc = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.clear(); int len = 0; while ((len = sc.read(buffer)) > 0) { buffer.flip(); sb.append(new String(buffer.array(), 0, len)); } if (sb.length() > 0) System.out.println("从客户端发来的数据:" + sb.toString()); if ("exit".equals(sb.toString().trim())||sb.length()==0) { sc.write(ByteBuffer.wrap("bye".getBytes())); System.out.println("服务端已经关闭"); key.cancel(); sc.close(); sc.socket().close(); } else { String msg = sc.socket().getRemoteSocketAddress() + ":" + sb.toString(); Iterator<SelectionKey> it = key.selector().keys().iterator(); // 把数据分发到每一个已经连接的客户端 while (it.hasNext()) { SelectionKey skey = it.next(); if (skey != key && skey != selectionKey) { RWriter myWriter = new RWriter(skey, msg); service.execute(myWriter); } } } } public static void main(String[] args) { RSocketServer server = new RSocketServer(); new Thread(server).start(); } class RWriter implements Runnable { SelectionKey key; String msg; public RWriter(SelectionKey key, String msg) { this.key = key; this.msg = msg; } public void run() { try { SocketChannel client = (SocketChannel) key.channel(); client.write(ByteBuffer.wrap(msg.getBytes())); Thread.yield(); } catch (IOException ex) { Logger.getLogger(RWriter.class.getName()).log(Level.SEVERE, null, ex); } } } private void RWriter(SelectionKey key) throws IOException { SocketChannel sc = (SocketChannel) key.channel(); String str = (String) key.attachment(); sc.write(ByteBuffer.wrap(str.getBytes())); key.interestOps(SelectionKey.OP_READ); } }
(三)、 Java NIO的Reactor模式
上面的例子,有没有发觉一些问题呢?会不会觉得例子有点“乱七八糟”的感觉,接收数据、业务逻辑、发送数据、数据的包装等等,这些都需要我们去处理。但是往往我们只是对业务逻辑感兴趣。于是我们能不能只是处理业务逻辑,而其他的都包装好(就像java web中的Servlet,我们只需要重写doPost()/doGet()来处理业务逻辑,其他的都不用管)?带着这些问题去google了一下,但是结果是建议使用一些java nio的框架(Mina、Cindy等),因为这些已经封装好了,只是使用一些API就行了。这样也是一种方法,但是,问题是不知道如何进行底层的实现。经过这段日子带着问题查阅资料,原来才知道他们是借Java NIO的Reactor模式来实现的,只是对现有的Reactor模式架构做了一层封装。那何为Reactor模式呢?(下面有些地方参考《Scalable IO in java》、(http://www.jdon.com/concurrent/nio.pdf))
(1)、Reactor模式中的几个概念:
①、Reactor(反应器):负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理。
②、Handlers(句柄):负责处理非阻塞的行为,标识系统管理的资源;同时将handlers与事件绑定;
③、Event Handler(事件处理器):以特定的方式来处理相应的事件。
下面图为基于Java NIO的Reactor模式
(2)、Java实现NIO Reactor模式
①、Reactor类,实现NIO的创建以及事件的监听。(关键代码)
selector = Selector.open();
。。。(略,上面的例子中有)
SelectionKey sk = serverSocket.register(…);
//这里利用sk的attach绑定Acceptor,如果有事件,则触//发Acceptor,相当于swing中的addListener(Listener)
Sk.attach(new Acceptor());
②、任务分派处理,相应运行任务。
Void dispatch(selectionKey key){
Runnable r = (Runnable)it.next();
if(r!=null)
r.run();
}
③、Reactor里内置Acceptor类,实现分派任务操作处理。
try{
SocketChannel c = serverSocket.accept();
if (c != null)
//调用Handler来处理channel
new SocketReadHandler(selector, c);
}
}
④、Handler类,负责处理请求。
Handler(Selector sel, SocketChannel c)
throws IOException {
socket = c;
socket.configureBlocking(false);
sk = socket.register(sel, 0);
//将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法
sk.attach(this);
//同时将SelectionKey标记为可读,以便读取。
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
了解了这些底层基础的知识,就可以学习一些NIO框架了,比如Apache Mina框架或者其他的等等。学起来也会比较的清晰。
(四)、 Java NIO 小结
(1)、Java NIO 与 流IO,在不同的方面各自有优势。在性能上,NIO系统比传统流IO有优势。但是如果我们要在服务器端实现同步通信、数据不必在读取前进行处理,那么可以考虑用流IO。如果服务端系统需要进行异步通信、有频繁的数据处理,那么可以考虑用NIO。
(2)、要想深入了解Java NIO的机制,先要了解他的基本概念,这个很重要。其次,要理解Java NIO的Reactor模式,那么多的java NIO框架都是基于他的,可以看出他的重要性和实用性。
这篇文章,我一年前就在百度文库那里发表过(Doc文档的形式),如果想下载,可以到http://wenku.baidu.com/view/2154350f581b6bd97f19eac0.html 下载。
相关推荐
NULL 博文链接:https://jimmyhr.iteye.com/blog/1837113
[第4节] JavaNIO流-通道1.flv [第5节] Java NIO流-通道2.flv [第6节] Java NIO流-socket通道操作.flv [第7节] Java NIO流-文件通道操作.flv [第8节] Java NIO流-选择器 .flv [第9节] Java NIO流-选择器操作.flv...
在Java NIO流篇1的学习中,我们将深入探讨NIO的核心概念和使用方法。 首先,我们从【第1节】 Java NIO流-简介和概述开始。这一部分主要介绍了NIO的基本理念和相对于BIO的优势。NIO的核心特点是它支持非阻塞I/O,这...
Java NIO(New IO)是Java 1.4版本引入的一个新模块,是对传统IO模型的补充和扩展。本教程将深入讲解Java NIO中的流和通道概念,以帮助开发者更好地理解和利用这一强大的功能。 首先,我们要理解Java NIO的核心组件...
Java的Socket编程和NIO(非阻塞I/O)可以构建可靠的通信机制。 6. **数据存储**:游戏往往需要持久化数据,如用户进度、成就等。学习如何使用数据库(如H2、SQLite)或者文件系统来存储和读取数据。 7. **性能优化...
Java语言基础教程-Java NIO流篇.txt 网盘永久链接 为方便java nio学习爱好者而上传
在本篇文章中,我们将深入探讨Java NIO如何读取文件。 一、NIO的基本概念 1. 缓冲区(Buffer):NIO的核心组件,用于存储数据。Java提供了多种Buffer类,如ByteBuffer、CharBuffer、IntBuffer等,分别对应不同数据...
本篇文章将深入探讨如何使用Java NIO(非阻塞I/O)来实现阻塞多线程通信,这对于高性能服务器端应用尤其重要。我们将会分析`EchoServer.java`、`EchoClient.java`和`SocketUtils.java`这三个文件中的关键知识点。 ...
Java NIO(New IO)是Java 1.4版本引入的一个新特性,是对传统IO模型的补充和扩展,提供了一种更高效的数据处理方式。在本教程中,我们将深入探讨Java NIO流的两个关键部分:文件通道操作和选择器。 ### 文件通道...
### Java NIO 核心概念详解 #### 一、Java NIO 基本介绍 Java NIO(New IO 或 NonBlocking IO)是 Java 1.4 版本开始引入的一种全新的 I/O API,旨在提高 I/O 吞吐量。与传统的阻塞 I/O 相比,NIO 的设计思想更为...
本篇文章将深入探讨Java NIO的基本概念、核心组件以及实际应用。 一、Java NIO概述 传统的Java I/O基于流(Stream)和缓冲区(Buffer)的模型,是阻塞式的,即在进行读写操作时会一直等待数据准备好或全部写入完成。而...
"Java Learning Path---资源篇"这个压缩包文件,很可能是提供了一系列有关Java学习的资料,包括但不限于教程、代码示例、实战项目和社区链接等。下面,我们将详细探讨Java学习中的关键知识点,并推荐一些可能在资源...
在本套"JAVA学习资料-高级篇"中,我们聚焦于Java编程的进阶主题,旨在帮助有经验的JAVA开发人员提升技能,深入理解Java平台的精髓。这份资源包含了一系列深度探讨Java特性和最佳实践的材料,对于那些已经在Java开发...
Java技能百练——网络篇是针对Java开发者在网络编程方面的深入学习和实践。在这个主题中,我们将探讨Java在处理网络通信、数据传输以及网络服务开发等多个关键领域的应用。下面,我们将详细解析Java网络编程的一些...
本资源包“Java项目开发实践---网络篇”涵盖了Java进行网络通信的基础与实践,旨在帮助开发者深入理解并掌握相关知识。 1. **Java网络编程基础** Java提供了丰富的API来处理网络通信,主要集中在`java.net`包下。...
本篇文章将深入探讨如何在Java NIO中使用Selector处理客户端的I/O请求。 首先,我们需要理解Selector的工作原理。Selector是一个多路复用器,它可以监控多个通道的事件状态,如连接就绪、数据可读或可写等。通过...
本篇将详细探讨Java NIO在写文件方面的应用。 1. **通道(Channels)** 在Java NIO中,数据的读取和写入都是通过通道(Channel)进行的。通道可以理解为连接到I/O设备的桥梁,例如文件、网络套接字等。要使用NIO写...
Java IO与NIO是Java平台中用于处理输入输出操作的核心技术。它们在处理数据传输、文件操作、网络通信等方面起着至关重要的作用。本篇将深入探讨这两个领域,旨在帮助开发者更好地理解和应用这些概念。 首先,Java ...