Channel (Java NIO)

【正文】netty源碼死磕1.3:  html

Java NIO Channel 編程

1. Java NIO Channel

1.1. Java NIO Channel的特色

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

(1)OIO流通常來講是單向的(只能讀或者寫),通道能夠讀也能夠寫。服務器

(2)OIO流值讀寫阻塞的,而通道能夠異步讀寫。網絡

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

1.2. Channel類型

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

(1)FileChannelsocket

(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羣文件共享。




無編程不創客,無案例不學習。瘋狂創客圈,一大波高手正在交流、學習中!

瘋狂創客圈 Netty 死磕系列 10多篇深度文章博客園 總入口】  QQ羣:104131248

相關文章
相關標籤/搜索