1. 程式人生 > >Java NIO學習筆記三------Chanel的四種實現篇

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()方法,就像在用傳統的通道一樣,只是在資料傳送方面沒有任何保證。