1. 程式人生 > >Channel (死磕3)

Channel (死磕3)

【正文】JAVA NIO 死磕3: 

Channel

1. Java NIO Channel

1.1. Java NIO Channel的特點

和老的OIO相比,通道和NIO流(非阻塞IO)主要有以下幾點區別:

(1)OIO流一般來說是單向的(只能讀或者寫),通道可以讀也可以寫。

(2)OIO流值讀寫阻塞的,而通道可以非同步讀寫。

(3)通道總是基於緩衝區Buffer來讀寫。

1.2. Channel型別

下面列出Java NIO中最重要的集中Channel的實現:

(1)FileChannel

(2)DatagramChannel

(3)SocketChannel

(4)ServerSocketChannel

四種通道的說明如下:

FileChannel用於檔案的資料讀寫。

DatagramChannel用於UDP的資料讀寫。

SocketChannel用於TCP的資料讀寫。

ServerSocketChannel允許我們監聽TCP連結請求,每個請求會建立會一個SocketChannel。

這個四種通道,涵蓋了 UDP 和 TCP網路 IO以及檔案 IO的操作。下面從通道的新建、讀取、寫入、關閉等四個操作,四種通道進行簡單的介紹。

1.3. FileChannel

FileChannel 是操作檔案的Channel,我們可以通過 FileChannel 從一個檔案中讀取資料,也可以將資料寫入到檔案中。

注意,FileChannel 不能設定為非阻塞模式。

操作一:開啟 FileChannel通道

RandomAccessFile aFile     = new RandomAccessFile("test.txt","rw");

FileChannel      inChannel = aFile.getChannel();

操作二:讀取資料

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

操作三:寫入資料

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);

}

操作四:關閉

channel.close();

當我們對 FileChannel 的操作完成後,必須將其關閉。

操作五:強制重新整理磁碟

channel.force(true);

FileChannel的force()方法將所有未寫入的資料從通道重新整理到磁碟中。在你呼叫該force()方法之前,出於效能原因,作業系統可能會將資料快取在記憶體中,因此您不能保證寫入通道的資料實際上寫入磁碟。

1.4. SocketChannel

有兩種Socket通道,一個是客戶端的SocketChannel,一個是負責伺服器端的Socket通道ServerSocketChannel。SocketChannel與OIO中的Socket類對應,ServerSocketChannel對應於OIO中的ServerSocket類相NIO。

兩種Socket通道新增的通道都支援阻塞和非阻塞兩種模式。在阻塞模式下的通道的建立、關閉、讀寫操作如下:

操作一:建立

SocketChannel socketChannel = SocketChannel.open();

socketChannel.connect(new InetSocketAddress("127.0.0.1",80));

這個是客戶端的建立。當一個伺服器端的ServerSocketChannel 接受到連線請求時,也會返回一個 SocketChannel 物件。

操作二:讀取

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = socketChannel.read(buf);

如果 read()返回 -1,那麼表示連線中斷了.

操作三:寫入資料

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.close();

在非阻塞模式,我們可以設定 SocketChannel 為非同步模式,這樣我們的 connect,read,write 都是非同步的了.

操作一:連線

socketChannel.configureBlocking(false);

socketChannel.connect(new InetSocketAddress("127.0.0.1",80));

while(! socketChannel.finishConnect() ){

    //wait,or do something else...

}

在非同步模式中,或許連線還沒有建立,socketChannel.connect 方法就返回了,因此我們不斷的自旋,檢查當前是否是連線到了主機。

操作二:非阻塞讀寫

在非同步模式下,讀寫的方式是一樣的.

在讀取時,因為是非同步的,因此我們必須檢查 read 的返回值,來判斷當前是否讀取到了資料.

ServerSocketChannel

ServerSocketChannel 顧名思義,是用在伺服器為端的,可以監聽客戶端的 TCP 連線,例如:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    //do something with socketChannel...

}

操作四:關閉

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.close();

1.4.1. 監聽連線

我們可以使用ServerSocketChannel.accept()方法來監聽客戶端的 TCP 連線請求,accept()方法會阻塞,直到有連線到來,當有連線時,這個方法會返回一個 SocketChannel 物件:

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    //do something with socketChannel...

}
1.4.2. 非阻塞模式

在非阻塞模式下,accept()是非阻塞的,因此如果此時沒有連線到來,那麼 accept()方法會返回null:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

serverSocketChannel.configureBlocking(false);

while(true){

    SocketChannel socketChannel =

            serverSocketChannel.accept();

    if(socketChannel != null){

        //do something with socketChannel...

        }

}

1.5. DatagramChannel

DatagramChannel 是用來處理 UDP 連線的.

操作一:開啟

DatagramChannel channel = DatagramChannel.open();

channel.socket().bind(new InetSocketAddress(9999));

操作二:讀取資料

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

channel.receive(buf);

操作三:傳送資料

String newData = "New String to write to file..."

                    + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();

buf.put(newData.getBytes());

buf.flip();

int bytesSent = channel.send(buf,new InetSocketAddress("example.com",80));

連線到指定地址

因為 UDP 是非連線的,因此這個的 connect 並不是向 TCP 一樣真正意義上的連線,因此我們僅僅可以從指定的地址中讀取或寫入資料.

channel.connect(new InetSocketAddress("example.com",80));


原始碼:


程式碼工程:  JavaNioDemo.zip

下載地址:在瘋狂創客圈QQ群檔案共享。


瘋狂創客圈:如果說Java是一個武林,這裡的聚集一群武痴, 交流程式設計體驗心得
QQ群連結:
瘋狂創客圈QQ群


無程式設計不創客,無案例不學習。 一定記得去跑一跑案例哦


JAVA NIO 死磕全目錄


1. JAVA NIO簡介
1.1. NIO 和 OIO 的對比
1.2. 阻塞和非阻塞
1.3. Channel
1.4. selector
1.5. Java NIO Buffer
2. Java NIO Buffer
2.1. Buffer型別的標記屬性
2.1.1. capacity
2.1.2. position
2.1.3. limit
2.1.4. 總結:
2.2. Buffer 型別
2.3. Buffer中的方法
2.3.1. 獲取allocate()方法
2.3.2. 寫put()方法
2.3.3. 讀切換flip()方法
2.3.4. 讀get() 方法
2.3.5. 倒帶rewind()方法
2.3.6. mark( )和reset( )
2.3.7. clear()清空
2.4. Buffer 的使用
2.4.1. 使用的基本步驟
2.4.2. 完整的例項程式碼
3. Java NIO Channel
3.1. Java NIO Channel的特點
3.2. Channel型別
3.3. FileChannel
3.4. SocketChannel
3.4.1. 監聽連線
3.4.2. 非阻塞模式
3.5. DatagramChannel
4. NIO Selector
4.1. Selector入門
4.1.1. Selector的和Channel的關係
4.1.2. 可選擇通道(SelectableChannel)
4.1.3. Channel註冊到Selector
4.1.4. 選擇鍵(SelectionKey)
4.2. Selector的使用流程
4.2.1. 建立Selector
4.2.2. 將Channel註冊到Selector
4.2.3. 輪詢查詢就緒操作
4.3. 一個NIO 程式設計的簡單例項