接上一篇JAVA NIO简单教程(二)
7.Java NIO FileChannel
Java NIO FileChannel 是连接到文件的通道,使用文件通道,您可以从文件中读取数据,并将数据写入文件。 Java NIO FileChannel 类是 NIO 使用标准 Java IO API 读取文件的替代方法。FileChannel 不能设置为非阻塞模式,它始终以阻塞模式运行。
打开文件通道
在您可以使用 FileChannel 之前,您必须打开它, 您不能直接使用 FileChannel。 您需要通过 InputStream、OutputStream 或 RandomAccessFile 获取 FileChannel。 以下是通过 RandomAccessFile 打开 FileChannel 的方法:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
从 FileChannel 读取数据
要从 FileChannel 读取数据,您可以调用 read() 方法之一。下面是一个例子:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
首先分配一个缓冲区。从 FileChannel 读取的数据被读入到 Buffer 中。其次,调用 FileChannel.read() 方法。该方法从 FileChannel 读取数据到 Buffer 中。 read() 方法返回的 int 表示有多少字节被写入缓冲区,如果返回 -1,则到达文件末尾。
将数据写入 FileChannel
将数据写入 FileChannel 是使用 FileChannel.write() 方法完成的,该方法将 Buffer 作为参数。下面是一个例子:
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
注意 FileChannel.write() 方法是如何在 while 循环中调用的。无法保证 write() 方法写入 FileChannel 的字节数。因此,我们重复 write() 调用,直到 Buffer 没有更多字节要写入。
FileChannel Position
读取或写入 FileChannel 时,您在特定位置执行此操作。您可以通过调用 position() 方法获取 FileChannel 对象的当前位置。您还可以通过调用 position(long pos) 方法来设置 FileChannel 的位置。
这里有两个例子:
long pos channel.position();
channel.position(pos +123);
如果您在文件结束后设置position,并尝试从channel读取,您将获得 -1 ( 文件结束标记)。
如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。
FileChannel的size方法
FileChannel实例的size()方法将返回该实例所关联文件的大小。如:
long fileSize = channel.size();
FileChannel的truncate方法
可以使用FileChannel.truncate()方法截取一个文件。截取文件时,你就是在给定的长度处将它截断。如:
channel.truncate(1024);
这个例子截取文件的前1024个字节。
FileChannel的force方法
FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。
force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。
下面的例子同时将文件数据和元数据强制写到磁盘上:
channel.force(true);
8.Java NIO SocketChannel
Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel:
- 1.打开一个SocketChannel并连接到互联网上的某台服务器。
- 2.一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。
打开 SocketChannel
下面是SocketChannel的打开方式:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.hhdatabase.com.cn", 80));
关闭 SocketChannel
当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel:
socketChannel.close();
从 SocketChannel 读取数据
要从SocketChannel中读取数据,调用一个read()的方法之一。以下是例子:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
首先,分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中。
然后,调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。
写入 SocketChannel
写数据到SocketChannel用的是SocketChannel.write()方法,该方法以一个Buffer作为参数。示例如下:
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
注意SocketChannel.write()方法的调用是在一个while循环中的。Write()方法无法保证能写多少字节到SocketChannel。所以,我们重复调用write()直到Buffer没有要写的字节为止。
非阻塞模式
可以设置 SocketChannel 为非阻塞模式(non-blocking mode),设置之后,就可以在异步模式下调用connect()、 read() 和write()了。
connect()
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));
while(! socketChannel.finishConnect() ){
//wait, or do something else...
}
write()
非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。前面已经有例子了,这里就不赘述了。
read()
非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。
非阻塞模式与选择器
非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,可以询问选择器哪个通道已经准备好了读取,写入等。