NIO是Java提供的非阻塞I/O API.
非阻塞的意义在于可以使用一个线程对大量的数据连接进行处理,非常适用于"短数据长连接"的应用场景,例如即时通讯软件.
在一个阻塞C/S系统中,服务器要为每一个客户连接开启一个线程阻塞等待客户端发送的消息.若使用非阻塞技术,服务器可以使用一个线程对连接进行轮询,无须阻塞等待.这大大减少了内存资源的浪费,也避免了服务器在客户线程中不断切换带来的CPU消耗,服务器对CPU的有效使用率大大提高.
其核心概念包括Channel,Selector,SelectionKey,Buffer.
Channel是I/O通道,可以向其注册Selector,应用成功可以通过select操作获取当前通道已经准备好的可以无阻塞执行的操作.这由SelectionKey表示.
SelectionKey的常量字段SelectionKey.OP_***分别对应Channel的几种操作例如connect(),accept(),read(),write().
select操作后得到SelectionKey.OP_WRITE或者READ即可在Channel上面无阻塞调用read和write方法,Channel的读写操作均需要通过Buffer进行.即读是讲数据从通道中读入Buffer然后做进一步处理.写需要先将数据写入Buffer然后通道接收Buffer.
下面是一个使用NIO的基本C/S示例.该示例只为显示如何使用基本的API而存在,其代码的健壮性,合理性都不具参考价值.
这个示例,实现一个简单的C/S,客户端想服务器端发送消息,服务器将收到的消息打印到控制台.现实的应用中需要定义发送数据使用的协议,以帮助服务器解析消息.本示例只是无差别的使用默认编码将收到的字节转换字符并打印.通过改变初始分配的ByteBuffer的容量,可以看到打印消息的变化.容量越小,对一条消息的处理次数就越多,容量大就可以在更少的循环次数内读完整个消息.所以真是的应用场景,要考虑适当的缓存大小以提高效率.
首先是Server
package hadix.demo.nio;
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.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* User: hAdIx
* Date: 11-11-2
* Time: 上午11:26
*/
public class Server {
private Selector selector;
private ByteBuffer readBuffer = ByteBuffer.allocate(8);//调整缓存的大小可以看到打印输出的变化
private Map<SocketChannel, byte[]> clientMessage = new ConcurrentHashMap<>();
public void start() throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress("localhost", 8001));
selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (!Thread.currentThread().isInterrupted()) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
}
keyIterator.remove();
}
}
}
private void read(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
// Clear out our read buffer so it's ready for new data
this.readBuffer.clear();
// Attempt to read off the channel
int numRead;
try {
numRead = socketChannel.read(this.readBuffer);
} catch (IOException e) {
// The remote forcibly closed the connection, cancel
// the selection key and close the channel.
key.cancel();
socketChannel.close();
clientMessage.remove(socketChannel);
return;
}
byte[] bytes = clientMessage.get(socketChannel);
if (bytes == null) {
bytes = new byte[0];
}
if (numRead > 0) {
byte[] newBytes = new byte[bytes.length + numRead];
System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
System.arraycopy(readBuffer.array(), 0, newBytes, bytes.length, numRead);
clientMessage.put(socketChannel, newBytes);
System.out.println(new String(newBytes));
} else {
String message = new String(bytes);
System.out.println(message);
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = ssc.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("a new client connected");
}
public static void main(String[] args) throws IOException {
System.out.println("server started...");
new Server().start();
}
}
然后是Client
package hadix.demo.nio;
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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
* User: hAdIx
* Date: 11-11-2
* Time: 上午11:26
*/
public class Client {
public void start() throws IOException {
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(new InetSocketAddress("localhost", 8001));
Selector selector = Selector.open();
sc.register(selector, SelectionKey.OP_CONNECT);
Scanner scanner = new Scanner(System.in);
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
System.out.println("keys=" + keys.size());
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if (key.isConnectable()) {
sc.finishConnect();
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println("server connected...");
break;
} else if (key.isWritable()) {
System.out.println("please input message");
String message = scanner.nextLine();
ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes());
sc.write(writeBuffer);
}
}
}
}
public static void main(String[] args) throws IOException {
new Client().start();
}
}
此外有一个代码写得更好的例子,非常值得参考.http://rox-xmlrpc.sourceforge.net/niotut/index.html
这个例子里面的客户端将消息发送给服务器,服务器收到后立即写回给客户端.例子中代码虽然也没有做有意义的处理,但是其结构比较合理,值得以此为基础进行现实应用的扩展开发.
分享到:
相关推荐
4. **管道(Pipe)**:在某些特定情况下,两个线程之间可以使用`java.nio.Pipe`进行单向数据传递。 5. **文件系统API**:NIO还提供了`java.nio.file`包,包含一系列与文件系统交互的类,如Files、Paths等。 Java ...
在探讨如何使用Java NIO实现Socket通信之前,我们需要先理解NIO(Non-blocking I/O,非阻塞I/O)与传统阻塞I/O之间的区别。 **传统阻塞I/O模型**:在传统的Java IO编程中,当我们调用`read()`或`write()`方法时,...
`NIOServer.java`和`NIOClient.java`这两个文件很可能是用于演示Java NIO服务器端和客户端的基本操作。下面将详细介绍Java NIO的主要组件和工作原理,并结合这两个文件名推测它们可能包含的内容。 1. **Selector...
例如,书中提到了使用 Java NIO 可以提高应用程序的响应性、可扩展性和可靠性等。 ### Java NIO API 与旧版 I/O API 的关系 值得注意的是,Java NIO API 是对旧版 I/O API 的补充而非替代。这意味着开发者需要了解...
二、使用Java NIO读取文件 在Java NIO中,读取文件主要涉及FileChannel和ByteBuffer。以下是一个简单的示例: ```java import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels....
本篇文章将深入探讨Java NIO的基本概念、核心组件以及实际应用。 一、Java NIO概述 传统的Java I/O基于流(Stream)和缓冲区(Buffer)的模型,是阻塞式的,即在进行读写操作时会一直等待数据准备好或全部写入完成。而...
缓冲区是Java NIO中非常重要的一个概念,它是特定基本类型元素的线性有限序列。缓冲区有四个基本属性:容量、限制、位置和标记。 * 容量(Capacity):缓冲区中元素的数量,不能为负数,不能更改。 * 限制(Limit)...
这些文件不直接与Java NIO知识相关,但它们反映了项目的基本结构,对于理解代码组织和开发环境配置有所帮助。 总的来说,学习和掌握Java NIO不仅可以提高程序的性能,还能帮助开发者设计出更加高效、可扩展的系统。...
在传统的Java I/O中,使用的是Blocking I/O,即阻塞式I/O,这种模型下,线程在等待数据就绪时会被挂起,直到数据准备好才能继续执行,这在处理大量并发连接时效率较低。而Java NIO则引入了选择器(Selectors)和通道...
### Java NIO 详细教程知识点解析 #### 一、Java NIO 概述 Java NIO(New IO)是Java平台提供的一种新的IO操作模式,它首次出现在Java 1.4版本中,并在后续版本中不断完善。Java NIO 的设计目的是为了克服传统Java ...
Java NIO(New IO)是Java 1.4版本引入的一个新模块,它提供了一种不同于传统IO(基于字节流和字符流)的I/O操作方式。传统的IO模型是阻塞式的,而NIO的核心特点是非阻塞,这使得在处理大量并发I/O请求时更为高效。...
通过上述示例代码,你可以了解到如何创建通道,如何创建和使用缓冲区,如何注册选择器,如何处理文件和网络通信等基本操作。这些示例通常会包含简单的读写文件、服务器端与客户端的通信以及多路复用的使用,帮助初学...
Java NIO(New Input/Output)是Java标准库中提供的一种I/O模型,与传统的BIO( Blocking I/O)相比,NIO...对于初学者来说,这些源码实例可以帮助理解Java NIO的基本用法和优势,进一步提升在实际项目中的应用能力。
Java NIO,全称为Non-...这个简单的例子展示了如何使用NIO进行基本的字符串通信和哈希计算,为理解和应用Java NIO提供了基础。在实际项目中,我们可以根据需求进一步优化,例如添加异常处理、使用多线程处理连接等。