1. 程式人生 > 程式設計 >5. 彤哥說netty系列之Java NIO核心元件之Channel

5. 彤哥說netty系列之Java NIO核心元件之Channel

nio

你好,我是彤哥,本篇是netty系列的第五篇。

簡介

上一章我們一起學習瞭如何使用Java原生NIO實現群聊系統,這章我們一起來看看Java NIO的核心元件之一——Channel。

思維轉變

首先,我想說的最重要的一個點是,學習NIO思維一定要從BIO那種一個連線一個執行緒的模式轉變成多個連線(Channel)共用一個執行緒來處理的這種思維。

nio

nio

1個Connection = 1個Socket = 1個Channel,這幾個概念可以看作是等價的,都表示一個連線,只不過是用在不同的場景中。

如果單從阻塞/非阻塞的角度來看的話,IO可以分成兩大類,一類是Blocking IO,一類是Non-blocking IO,像IO五種模型中的後四種其實都可以看作是非阻塞型IO,只是各自使用的手段不相同罷了。

在Java中,我們說的非阻塞IO或者說NIO(New IO)主要是指多路複用IO,底層可以使用select/poll/epoll等技術實現。

另外,在Java1.7的時候引入了NIO2,這個主要是指非同步IO模型,也就是我們常說的AIO,底層完全使用非同步回撥的方式來實現。

但是,由於AIO這項技術在linux作業系統上還不太成熟,所以我們通常也不會說太多關於這方面的內容。

在後面我們學習Netty的時候會再次講到這三種IO模型,可以看到Netty是完全支援三種IO的,但是它把OIO(BIO)和AIO都給deprecated了,也進一步說明瞭AIO的不成熟性。

好了,扯了一下思維轉變的問題,下面正式進入今天的內容——Java NIO核心元件之Channel

Channel

概念

我們先來看看Java中對於Channel的定義,位於java.nio.channels.Channel類的註釋上:

A nexus for I/O operations.

本文來源工從號彤哥讀原始碼

A channel represents an open connection to an entity such as a hardware device,a file,a network socket,or a program component that is capable of performing one or more distinct I/O operations,for example reading or writing.

第一句,它是IO操作的一種連線。

nexus,the means of connection between things linked in series.

第二句,Channel代表到實體的開放連線,這個實體可以是硬體,檔案,網路套接字,或者程式元件,並且可以執行一個或多個不同的IO操作,例如,讀或寫。

簡單點講,Channel就是實體與實體之間的連線,比如,操作檔案可以使用FileChannel,操作網路可以使用SocketChannel等。

與流的區別

BIO是面向流(Stream)程式設計的,流又分成InputStream和OutputStream,那麼Channel和Stream有什麼區別呢?

  • Channel可以同時支援讀和寫,而Stream只能支援單向的讀或寫(所以分成InputStream和OutputStream)
  • Channel支援非同步讀寫,Stream通常只支援同步
  • Channel總是讀向(read into)Buffer,或者寫自(write from)Buffer(有點繞,以Channel為中心,從Channel中讀出資料到Buffer,從Buffer中往Channel寫入資料)

nio

實現方式

下面列舉了JDK中比較重要的實現方式:

  • FileChannel:操作檔案
  • DatagramChannel:UDP協議支援
  • SocketChannel:TCP協議支援
  • ServerSocketChannel:監聽TCP協議Accept事件,之後建立SocketChannel

例子

public class FileChannelTest {
    public static void main(String[] args) throws IOException {
        // 從檔案獲取一個FileChannel
        FileChannel fileChannel = new RandomAccessFile("D:\\object.txt","rw").getChannel();
        // 宣告一個Byte型別的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        // 將FileChannel中的資料讀出到buffer中,-1表示讀取完畢
        // buffer預設為寫模式,本文來源工從號彤哥讀原始碼
        // read()方法是相對channel而言的,相對buffer就是寫
        while ((fileChannel.read(buffer)) != -1) {
            // buffer切換為讀模式
            buffer.flip();
            // buffer中是否有未讀資料
            while (buffer.hasRemaining()) {
                // 未讀資料的長度
                int remain = buffer.remaining();
                // 宣告一個位元組陣列
                byte[] bytes = new byte[remain];
                // 將buffer中資料讀出到位元組陣列中
                buffer.get(bytes);
                // 打印出來
                System.out.println(new String(bytes,StandardCharsets.UTF_8));
            }
            // 清空buffer,為下一次寫入資料做準備
            // clear()會將buffer再次切換為寫模式
            buffer.clear();
        }
    }
}複製程式碼

可以看到,Channel與Buffer是息息相關的。注意這裡的讀寫方法,呼叫者是誰就以誰為核心,channel.read()就表示從channel讀出資料,buffer.get()就表示從buffer讀出資料,這跟傳統程式設計的角度不太一樣的地方。

總結

今天我們學習了Java NIO核心元件之Channel,它與傳統BIO中的流很類似但又有所區別,且經常與Buffer聯合起來使用,Buffer又是什麼呢?請聽下回分解。

參考

挺不錯的一個網站:

http://tutorials.jenkov.com/java-nio/channels.html

最後,也歡迎來我的工從號彤哥讀原始碼系統地學習原始碼&架構的知識。

nio