接上一篇JAVA NIO簡(jiǎn)單教程(二)
7.Java NIO FileChannel
Java NIO FileChannel 是連接到文件的通道,使用文件通道,您可以從文件中讀取數(shù)據(jù),并將數(shù)據(jù)寫(xiě)入文件。 Java NIO FileChannel 類是 NIO 使用標(biāo)準(zhǔn) Java IO API 讀取文件的替代方法。FileChannel 不能設(shè)置為非阻塞模式,它始終以阻塞模式運(yùn)行。
打開(kāi)文件通道
在您可以使用 FileChannel 之前,您必須打開(kāi)它, 您不能直接使用 FileChannel。 您需要通過(guò) InputStream、OutputStream 或 RandomAccessFile 獲取 FileChannel。 以下是通過(guò) RandomAccessFile 打開(kāi) FileChannel 的方法:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
從 FileChannel 讀取數(shù)據(jù)
要從 FileChannel 讀取數(shù)據(jù),您可以調(diào)用 read() 方法之一。下面是一個(gè)例子:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
首先分配一個(gè)緩沖區(qū)。從 FileChannel 讀取的數(shù)據(jù)被讀入到 Buffer 中。其次,調(diào)用 FileChannel.read() 方法。該方法從 FileChannel 讀取數(shù)據(jù)到 Buffer 中。 read() 方法返回的 int 表示有多少字節(jié)被寫(xiě)入緩沖區(qū),如果返回 -1,則到達(dá)文件末尾。
將數(shù)據(jù)寫(xiě)入 FileChannel
將數(shù)據(jù)寫(xiě)入 FileChannel 是使用 FileChannel.write() 方法完成的,該方法將 Buffer 作為參數(shù)。下面是一個(gè)例子:
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 循環(huán)中調(diào)用的。無(wú)法保證 write() 方法寫(xiě)入 FileChannel 的字節(jié)數(shù)。因此,我們重復(fù) write() 調(diào)用,直到 Buffer 沒(méi)有更多字節(jié)要寫(xiě)入。
FileChannel Position
讀取或?qū)懭?FileChannel 時(shí),您在特定位置執(zhí)行此操作。您可以通過(guò)調(diào)用 position() 方法獲取 FileChannel 對(duì)象的當(dāng)前位置。您還可以通過(guò)調(diào)用 position(long pos) 方法來(lái)設(shè)置 FileChannel 的位置。
這里有兩個(gè)例子:
long pos channel.position();
channel.position(pos +123);
如果您在文件結(jié)束后設(shè)置position,并嘗試從channel讀取,您將獲得 -1 ( 文件結(jié)束標(biāo)記)。
如果將位置設(shè)置在文件結(jié)束符之后,然后向通道中寫(xiě)數(shù)據(jù),文件將撐大到當(dāng)前位置并寫(xiě)入數(shù)據(jù)。這可能導(dǎo)致“文件空洞”,磁盤(pán)上物理文件中寫(xiě)入的數(shù)據(jù)間有空隙。
FileChannel的size方法
FileChannel實(shí)例的size()方法將返回該實(shí)例所關(guān)聯(lián)文件的大小。如:
long fileSize = channel.size();
FileChannel的truncate方法
可以使用FileChannel.truncate()方法截取一個(gè)文件。截取文件時(shí),你就是在給定的長(zhǎng)度處將它截?cái)?。如?/p>
channel.truncate(1024);
這個(gè)例子截取文件的前1024個(gè)字節(jié)。
FileChannel的force方法
FileChannel.force()方法將通道里尚未寫(xiě)入磁盤(pán)的數(shù)據(jù)強(qiáng)制寫(xiě)到磁盤(pán)上。出于性能方面的考慮,操作系統(tǒng)會(huì)將數(shù)據(jù)緩存在內(nèi)存中,所以無(wú)法保證寫(xiě)入到FileChannel里的數(shù)據(jù)一定會(huì)即時(shí)寫(xiě)到磁盤(pán)上。要保證這一點(diǎn),需要調(diào)用force()方法。
force()方法有一個(gè)boolean類型的參數(shù),指明是否同時(shí)將文件元數(shù)據(jù)(權(quán)限信息等)寫(xiě)到磁盤(pán)上。
下面的例子同時(shí)將文件數(shù)據(jù)和元數(shù)據(jù)強(qiáng)制寫(xiě)到磁盤(pán)上:
channel.force(true);
8.Java NIO SocketChannel
Java NIO中的SocketChannel是一個(gè)連接到TCP網(wǎng)絡(luò)套接字的通道??梢酝ㄟ^(guò)以下2種方式創(chuàng)建SocketChannel:
- 1.打開(kāi)一個(gè)SocketChannel并連接到互聯(lián)網(wǎng)上的某臺(tái)服務(wù)器。
- 2.一個(gè)新連接到達(dá)ServerSocketChannel時(shí),會(huì)創(chuàng)建一個(gè)SocketChannel。
打開(kāi) SocketChannel
下面是SocketChannel的打開(kāi)方式:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.hhdatabase.com.cn", 80));
關(guān)閉 SocketChannel
當(dāng)用完SocketChannel之后調(diào)用SocketChannel.close()關(guān)閉SocketChannel:
socketChannel.close();
從 SocketChannel 讀取數(shù)據(jù)
要從SocketChannel中讀取數(shù)據(jù),調(diào)用一個(gè)read()的方法之一。以下是例子:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);
首先,分配一個(gè)Buffer。從SocketChannel讀取到的數(shù)據(jù)將會(huì)放到這個(gè)Buffer中。
然后,調(diào)用SocketChannel.read()。該方法將數(shù)據(jù)從SocketChannel 讀到Buffer中。read()方法返回的int值表示讀了多少字節(jié)進(jìn)Buffer里。如果返回的是-1,表示已經(jīng)讀到了流的末尾(連接關(guān)閉了)。
寫(xiě)入 SocketChannel
寫(xiě)數(shù)據(jù)到SocketChannel用的是SocketChannel.write()方法,該方法以一個(gè)Buffer作為參數(shù)。示例如下:
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()方法的調(diào)用是在一個(gè)while循環(huán)中的。Write()方法無(wú)法保證能寫(xiě)多少字節(jié)到SocketChannel。所以,我們重復(fù)調(diào)用write()直到Buffer沒(méi)有要寫(xiě)的字節(jié)為止。
非阻塞模式
可以設(shè)置 SocketChannel 為非阻塞模式(non-blocking mode),設(shè)置之后,就可以在異步模式下調(diào)用connect()、 read() 和write()了。
connect()
如果SocketChannel在非阻塞模式下,此時(shí)調(diào)用connect(),該方法可能在連接建立之前就返回了。為了確定連接是否建立,可以調(diào)用finishConnect()的方法。像這樣:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));
while(! socketChannel.finishConnect() ){
//wait, or do something else...
}
write()
非阻塞模式下,write()方法在尚未寫(xiě)出任何內(nèi)容時(shí)可能就返回了。所以需要在循環(huán)中調(diào)用write()。前面已經(jīng)有例子了,這里就不贅述了。
read()
非阻塞模式下,read()方法在尚未讀取到任何數(shù)據(jù)時(shí)可能就返回了。所以需要關(guān)注它的int返回值,它會(huì)告訴你讀取了多少字節(jié)。
非阻塞模式與選擇器
非阻塞模式與選擇器搭配會(huì)工作的更好,通過(guò)將一或多個(gè)SocketChannel注冊(cè)到Selector,可以詢問(wèn)選擇器哪個(gè)通道已經(jīng)準(zhǔn)備好了讀取,寫(xiě)入等。