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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM