Java NIO Channel (netty源碼死磕1.3)

【基礎篇】netty 源碼死磕3: 

JAVA NIO  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 編程的簡單實例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章