NIO 之 Channel實現原理

相關文章

BIO、NIO、AIO 內部原理分析
NIO 之 Selector實現原理
NIO 之 ByteBuffer實現原理數組

NIO概述

Java NIO 由如下幾個核心部分組成:服務器

  • Channels
  • Buffers
  • Selectors

在傳統IO中,流是基於字節的方式進行讀寫的。
在NIO中,使用通道(Channel)基於緩衝區數據塊的讀寫。網絡

Channel 和 IO 流的區別

Java NIO的通道相似IO中的流,但又有些不一樣:異步

  1. 既能夠從通道中讀取數據,又能夠寫數據到通道。但流的讀寫一般是單向的。
  2. 通道能夠異步地讀寫。
  3. 通道中的數據老是要先讀到一個Buffer,或者老是要從一個Buffer中寫入。

Channel 實現類

下面是JAVA NIO中的一些主要Channel的實現:socket

  1. FileChannel
    FileChannel 從文件中讀寫數據。
  2. DatagramChannel
    DatagramChannel 能經過UDP讀寫網絡中的數據。
  3. SocketChannel
    SocketChannel 能經過TCP讀寫網絡中的數據。
  4. ServerSocketChannel
    ServerSocketChannel能夠監聽新進來的TCP鏈接,像Web服務器那樣。對每個新進來的鏈接都會建立一個SocketChannel。

FileChannel 是基於文件的Channel。
DatagramChannel、SocketChannel、ServerSocketChannel 都是基於網絡流的Channel。編碼

下面咱們主要介紹關於基於網絡流的SocketChannel 和 ServerSocketChannel 。spa

register 方法

基於網絡流的Channel中提供了register方法。
使用示例:.net

channel.register(selector,  Selectionkey.OP_READ, ByteBuffer.allocate(64));

代碼的寫法感受是要將selector對象註冊到channel中。其實正好相反,應該是將channel註冊到selector中。
下面經過代碼分析:
![將channel 註冊到 selector 中]code

channel 的 register 方法server

1.首先判斷 selector 中是否已經註冊此 channel ,若是註冊過就更新註冊的事件和attach附件信息。

  1. 若是 selector 沒有註冊過 channel ,則將 channel 註冊到 selector 中。

configureBlocking 方法

public final SelectableChannel configureBlocking(boolean block)

Channel 默認使用阻塞模式,經過 configureBlocking 方法能夠將該 Channel 設置成非阻塞模式。

//設置成非阻塞模式
channel.configureBlocking(false);

ServerSocketChannel

ServerSocketChannel只支持入站鏈接請求。不提供讀取、寫入數據功能。

accept 方法

public abstract SocketChannel accept()

accept()能夠在阻塞或非阻塞模式下操做。

  • 阻塞模式
    accept()方法等待入站鏈接。而後它接受一個鏈接,並返回到遠程客戶端的一個SocketChannel。在創建鏈接前你沒法作任何操做。

示例:

public static void blockMode(){
    try {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //綁定要監控的端口
        serverChannel.bind(new InetSocketAddress(9000));
        //獲取入站請求socketChannel
        SocketChannel clientChannel = serverChannel.accept();
        ...
    } catch (Exception e) {
    }
}
  • 非阻塞模式
    若是沒有入站鏈接,accept()返回null。非阻塞模式通常和Selector結合使用。

示例:

public static void nonBlockMode(){
    try {
        //建立一個selector
        Selector selector = Selector.open();
        //建立一個ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //設置成非阻塞模式
        serverChannel.configureBlocking(false);
        //綁定要監控的端口
        serverChannel.bind(new InetSocketAddress(9000));
        //接受入站請求
        serverChannel.accept();
        //將 serverChannel 註冊到 selector 上
        serverChannel.register(selector, SelectionKey.OP_READ);
    } catch (Exception e) {
    }
}

SocketChannel

SocketChannel 類能夠讀寫TCP Socket。數據必須編碼到ByteBuffer對象中來完成讀寫。

open 方法

  1. public static SocketChannel open()
    建立一個SocketChannel,但不鏈接(沒有指定目標ip和port),若是想使用非阻塞模式則使用該方法建立SocketChannel對象。

示例:

public static void nonSocketChannel() throws Exception {
    SocketChannel client = SocketChannel.open();
    client.configureBlocking(false);
    client.connet(new InetSocketAddress("192.168.1.10", 9000));
}
  1. public static SocketChannel open(SocketAddress remote)
    構造SocketChannel 對象,並使用阻塞模式鏈接目標地址。

connect 方法

public abstract boolean connect(SocketAddress remote)

非阻塞模式下,若是要鏈接遠程服務器必須使用 connect 方法進行鏈接。

finishConnect 方法

public abstract boolean finishConnect()

阻塞模式:直接返回true,由於在構造SocketChannel的時候已經創建鏈接了。
非阻塞模式下:必須調用此方法來判斷該方法是否已經創建網絡鏈接,只有創建網絡鏈接後才能進行讀寫操做。

判斷網絡鏈接狀態

public abstract boolean isConnected()
判斷是否已經鏈接。
public abstract boolean isConnectionPending()
判斷鏈接是否正在鏈接狀態

read 方法

  • public int read(ByteBuffer buf)
    將 SocketChannel 中的數據讀入填充到 buf 中
  • public final long read(ByteBuffer[] dsts)
    將 SocketChannel 中的數據讀入填充到 dsts[] 數組。按照數組順序進行填充。
  • public long read(ByteBuffer[] dsts, int offset, int length)
    將 SocketChannel 中的數據填充到 dsts[] 數組中,從第數組中的offset座標開始填充,填充 length 個Bytebuffer。

write 方法

  • public int write(ByteBuffer src)
    將 buf 中的數據寫入到 SocketChannel 中
  • public final long write(ByteBuffer[] srcs)
    將 srcs[] 數組中的數據寫入到 SocketChannel 中,按照數組順序一個一個寫入。
  • public long write(ByteBuffer[] srcs, int offset, int length) 將 srcs[] 數組中的數據寫入到 SocketChannel 中,從數組中 offset 座標開始,寫入length個ByteBuffer對象。
相關文章
相關標籤/搜索