Java NIO學習筆記三------Chanel的四種實現篇
FileChannel
FileChannel是什麼
FileChannel是一個連線到檔案的通道,可以通過檔案通道讀寫檔案。它無法設定為非阻塞模式,總是執行在阻塞模式下。
開啟FileChannel
我們可以通過使用一個InputStream、OutputStream或RandomAccessFile來獲取一個FileChannel例項。例如:
RandomAccessFile aFile = new RandomAccessFile("D:/demo/data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
讀FileChannel
呼叫FileChannel.read()方法,例如:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
read()方法返回的int值表示了有多少位元組被讀到了Buffer中。如果返回-1,表示到了檔案末尾。
寫FileChannel
呼叫FileChannel.write()方法,例如:
String newData = "Some data"; ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); buf.put(newData.getBytes()); buf.flip(); while(buf.hasRemaining()) { channel.write(buf); }
因為無法保證write()方法一次能向FileChannel寫入多少位元組,因此需要重複呼叫write()方法,直到Buffer中已經沒有尚未寫入通道的位元組。
關閉FileChannel
用完FileChannel後必須將其關閉,例如:
channel.close();
FileChannel的位置
呼叫position()方法可以獲取FileChannel的當前位置,呼叫position(long pos)方法設定FileChannel的當前位置。例如:
long pos = channel.position();
channel.position(pos + 100);
如果將位置設定在檔案結束符之後,然後從通道中讀資料,讀方法將返回-1 ,也就是檔案結束標誌。
如果將位置設定在檔案結束符之後,然後向通道中寫資料,檔案將撐大到當前位置並寫入資料。這可能導致“檔案空洞”,磁碟上物理檔案中寫入的資料間有空隙。
獲取檔案的大小
long fileSize = channel.size();
擷取檔案
呼叫FileChannel.truncate()方法,擷取檔案時,檔案中指定長度後面的部分將被刪除。如:
channel.truncate(1024);
強制寫入磁碟
作業系統一般會將資料快取在記憶體中,寫入到FileChannel裡的資料不一定會即時寫到磁碟上。呼叫force()方法可以將通道里尚未寫入磁碟的資料強制寫到磁碟上。
force()方法有一個boolean型別的引數,true表示同時將檔案元資料(許可權資訊等)寫到磁碟上,例如:
channel.force(true);
SocketChannel
SocketChannel是什麼
SocketChannel是一個連線到TCP網路套接字的通道。
開啟 SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
關閉 SocketChannel
socketChannel.close();
讀SocketChannel
同讀FileChannel。
寫SocketChannel
同寫FileChannel。
非阻塞模式下的連線
非阻塞模式下呼叫connect(),該方法可能在連線建立之前就返回了。為了確定連線是否建立,可以呼叫finishConnect()方法。
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
while(!socketChannel.finishConnect() ){
// wait, or do something else
}
非阻塞模式下的讀
非阻塞模式下,read()方法在尚未讀到任何資料時可能就返回了。所以需要關注它的int返回值,它會告訴你讀取了多少位元組。
非阻塞模式下的寫
非阻塞模式下,write()方法在尚未寫出任何資料時可能就返回了。所以需要在迴圈中呼叫write()。
ServerSocketChannel
ServerSocketChannel是什麼
ServerSocketChannel 是一個可以監聽新進來的TCP連線的通道,就像標準IO中的ServerSocket一樣。
開啟 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
關閉 ServerSocketChannel
serverSocketChannel.close();
監聽新進來的連線
用ServerSocketChannel.accept()方法監聽新進來的連線。當 accept()方法返回的時候,它返回一個包含新進來的連線的 SocketChannel。因此,accept()方法會一直阻塞到有新連線到達。
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
// do something
}
如果是非阻塞模式,accept()方法會立刻返回,如果還沒有新進來的連線,返回的將是null。 因此,需要檢查返回的SocketChannel是否是null.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(80));
serverSocketChannel.configureBlocking(false);
while(true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
//do something
}
}
DatagramChannel
DatagramChannel是什麼
DatagramChannel是一個能收發UDP包的通道。因為UDP是無連線的網路協議,所以不能像其它通道那樣讀取和寫入。它傳送和接收的是資料包。
開啟 DatagramChannel
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9000));
接收資料
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
傳送資料
String newData = "Some data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("www.xxx.com", 9000));
連線
由於UDP是無連線的,連線到特定地址並不會像TCP通道那樣建立一個真正的連線,而是鎖住DatagramChannel ,讓其只能從特定地址收發資料。
channel.connect(new InetSocketAddress("www.xxx.com", 9000));
當連線後,也可以使用read()和write()方法,就像在用傳統的通道一樣,只是在資料傳送方面沒有任何保證。