概述
在 Java NIO 中,基本上所有的 IO 操作都是从 Channel 开始。程序可以通过 Channel 读取文件中的内容并写到 Buffer 中(对于 Channel 来说是调用 read(),对于 Buffer 来说是写过程),也可以通过 Channel 读取 Buffer 中的内容并写到文件中(对于 Channel 来说是调用write(),对于 Buffer 来说是读过程)如下图所示:
形象的描述
在上一期 Buffer 详解的示例中,我们演示过了如何将一个文件的内容拷贝到另一个文件中。我们可以将源文件理解为一座矿山,目标文件理解为目的地,我们的程序就是一个中转站。我们要做的就是:
- 打通两端的隧道,一个用于输入(从源矿山到中转站),一个用于输出(从中转站到目的地)。
FileChannel in = new FileInputStream(args[0]).getChannel(),
out = new FileOutputStream(args[1]).getChannel();
- 打造一列用来运矿的矿车,制定它的大小,他有多少节车厢。
ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
- 用于输入的隧道将矿装进矿车,并运到中转站。最后要在车箱上做好标记,确定卸货时,从哪节车箱开始,从哪节结束。
in.read(buffer);
buffer.flip();
- 用于输出的隧道将矿运到目的地,并卸货。最后要在车箱上做好标记,确定可以下次装货时,可以从头装到尾。
out.write(buffer);
buffer.clear();
- 如果还有需要运输的矿,重复3和4。
while(in.read(buffer)!=-1) {
//in调用read()后,数据输入到buffer,buffer内部指针位置改变,
// buffer就要调用flip(),对内部指针重新安排,以便out.write()提取。
buffer.flip();
out.write(buffer);
//out调用write()后,数据仍在buffer中,buffer内部指针位置改变,
//clear()对内部指针重新安排,以便buffer在另一个in.read()操作期间能够做好接收数据的准备。
buffer.clear();
}
- 运输完毕,关闭隧道。
in.close();
out.close();
NIO Channel 对比 IO Stream
- 对于同一个 Channel ,我们可以从它读取数据,也可以向它写入数据。而对于同一个 Stream ,通常要么只能读,要么只能写,二选一。
- Channel 可以非阻塞的读写 IO 操作,而 Stream 只能阻塞的读写 IO 操作。
- Channel 必须配合 Buffer 使用,总是先读取到一个 Buffer 中,又或者是向一个 Buffer 写入。也就是说,我们无法绕过 Buffer ,直接向 Channel 写入数据。
Channel 的实现
Channel为一个抽象接口:
public interface Channel extends Closeable {
public boolean isOpen();
public void close() throws IOException;
}
它有4个重要的实现:
- SocketChannel :一个客户端用来发起 TCP 的 Channel 。
- ServerSocketChannel :一个服务端用来监听新进来的连接的 TCP 的 Channel 。对于每一个新进来的连接,都会创建一个对应的 SocketChannel 。
- DatagramChannel :通过 UDP 读写数据。
- FileChannel :从文件中,读写数据。