自从 J2SE 1.4 版本以来, JDK 发布了全新的 I/O 类库,简称 NIO ,其不但引入了全新的高效的 I/O 机制,同时,也引入了多路复用的异步模式。 NIO 的包中主要包含了这样几种抽象数据类型:
Buffer :包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的 I/O 操作。
Charset :它提供 Unicode 字符串影射到字节序列以及逆映射的操作。
Channels :包含 socket , file 和 pipe 三种管道,都是全双工的通道。
Selector :多个异步 I/O 操作集中到一个或多个线程中(可以被看成是 Unix 中 select() 函数的面向对象版本)。
看看IO与NIO的结构对比:
以下是一个简单实例:
package com;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
public class NIOServer {
// 定义缓存大小
private int BLOCK = 4096;
// 定义监视器
private Selector selector;
private String filename = "E:\\Hibernate3.xml";
// 定义客户端缓存
private ByteBuffer clientBuffer = ByteBuffer.allocate(BLOCK);
// 定义编码器
private CharsetDecoder decoder;
public NIOServer(int port) throws IOException {
this.selector = this.getSelector(port);
Charset charset = Charset.forName("GB2312");
this.decoder = charset.newDecoder();
}
// 获取Selector
protected Selector getSelector(int port) throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
Selector sel = Selector.open();
// 将服务端套接字通道绑定到指定端口上
server.socket().bind(new InetSocketAddress(port));
server.configureBlocking(false);
// 将服务端套接字通道注册到监视器上,监视客户端的请求操作.
server.register(sel, SelectionKey.OP_ACCEPT);
return sel;
}
// 处理与客户端的交互
public class HandleClient {
protected FileChannel channel;
protected ByteBuffer buffer;
public HandleClient() throws IOException {
this.channel = new FileInputStream(filename).getChannel();
this.buffer = ByteBuffer.allocate(BLOCK);
}
public ByteBuffer readBlock() {
try {
buffer.clear();
int count = channel.read(buffer);
buffer.flip();
if (count <= 0)
return null;
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
public void close() {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 监听端口
public void listen() {
try {
for (;;) {
selector.select();
// 得到所有监视对象的KEY,查看通道的操作状态
Iterator<SelectionKey> iter = selector.selectedKeys()
.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
handleKey(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 处理事件
protected void handleKey(SelectionKey key) throws IOException {
if (key.isAcceptable()) { // 接收请求
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 当调用accept()时,IO阻塞直到建立连接并返回SocketChannel.
SocketChannel channel = server.accept();
// 设定非阻塞式IO处理方式.
channel.configureBlocking(false);
// 将通道注册到Selector,并由Selector监视通道的状态.
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) { // 读信息
SocketChannel channel = (SocketChannel) key.channel();
int count = channel.read(clientBuffer);
if (count > 0) {
clientBuffer.flip();
CharBuffer charBuffer = decoder.decode(clientBuffer);
System.out.println("Client >>" + charBuffer.toString());
SelectionKey wKey = channel.register(selector,
SelectionKey.OP_WRITE);
wKey.attach(new HandleClient());
} else
channel.close();
clientBuffer.clear();
} else if (key.isWritable()) { // 写事件
SocketChannel channel = (SocketChannel) key.channel();
HandleClient handle = (HandleClient) key.attachment();
ByteBuffer block = handle.readBlock();
if (block != null)
channel.write(block);
else {
handle.close();
channel.close();
}
}
}
public static void main(String[] args) {
int port = 12345;
try {
NIOServer server = new NIOServer(port);
System.out.println("Listernint on " + port);
while (true) {
server.listen();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NIOClient {
private static int SIZE = 10;
private static InetSocketAddress ip = new InetSocketAddress("localhost",
12345);
private static CharsetEncoder encoder = Charset.forName("GB2312")
.newEncoder();
static class Download implements Runnable {
protected int index;
public Download(int index) {
this.index = index;
}
public void run() {
try {
long start = System.currentTimeMillis();
// 建立一个套接字通道
SocketChannel client = SocketChannel.open();
// 设定为非阻塞式
client.configureBlocking(false);
// 打开一个监视器
Selector selector = Selector.open();
// 注册连接请求事件到监视器上
client.register(selector, SelectionKey.OP_CONNECT);
// 让客户端连接服务端
client.connect(ip);
ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
int total = 0;
FOR: for (;;) {
selector.select();
// 得到所有监视对象的KEY,查看通道的操作状态
Iterator<SelectionKey> iter = selector.selectedKeys()
.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key
.channel();
if (channel.isConnectionPending())
channel.finishConnect();
channel.write(encoder.encode(CharBuffer
.wrap("Hello from " + index)));
// 再次向监视器注册通道的读取状态的监视
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key
.channel();
int count = channel.read(buffer);
if (count > 0) {
total += count;
buffer.clear();
} else {
client.close();
break FOR;
}
}
}
}
double last = (System.currentTimeMillis() - start) * 1.0 / 1000;
System.out.println("Thread " + index + " downloaded " + total
+ "bytes in " + last + "s.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
ExecutorService exec = Executors.newFixedThreadPool(SIZE);
for (int index = 0; index < SIZE; index++) {
exec.execute(new Download(index));
}
exec.shutdown();
}
}