Netty基礎-NIO(一)

零拦惋、本文綱要

一、NIO三大組件

  1. Channel
  2. Buffer
  3. Selector

二、Buffer

  1. 基礎依賴
  2. ByteBuffer使用
  3. ByteBuffer結構
  4. ByteBuffer常見方法

三顾彰、Buffer使用模擬

  1. 情景模擬
  2. 模擬還原數據

一、NIO三大組件

NIO胃碾,non-blocking io 非阻塞 IO

Channel / Buffer / Selector

1. Channel

雙向通道涨享,可以從channel將數據讀入buffer,也可以將buffer的數據寫入channel仆百;
與stream對比厕隧,stream是單向的,要么輸入要么輸出俄周。

常見的Channel:
FileChannel / DatagramChannel / SocketChannel / ServerSocketChannel

2. Buffer

用來緩沖讀寫數據吁讨。

常見的Buffer:
ByteBuffer(MappedByteBuffer/DirectByteBuffer/HeapByteBuffer) /
ShortBuffer / IntBuffer / LongBuffer / FloatBuffer / DoubleBuffer / CharBuffer

3. Selector

① 多線程處理多個Socket連接

單個Thread對應單個Socket

內存占用高 / 線程上下文切換成本高 / 僅適合【連接數少】的場景

② 線程池處理多個Socket連接

單個Thread可以處理多個Socket

阻塞模式下線程只能處理一個Socket / 僅適合【短連接】的場景

③ selector配合線程處理多個Socket

selector 的作用就是配合一個線程來管理多個 channel,獲取這些 channel 上發(fā)生的事件栈源,這些 channel 工作在非阻塞模式下挡爵,不會讓線程吊死在一個 channel 上竖般。
適合連接數特別多甚垦,但流量低的場景(low traffic)。

調用 selector 的 select() 會阻塞直到 channel 發(fā)生了讀寫就緒事件涣雕,這些事件發(fā)生艰亮,select 方法就會返回這些事件交給 thread 來處理。

二挣郭、Buffer

0. 基礎依賴

netty-all           4.1.39.Final
lombok              1.16.18
gson                2.8.5
guava               19.0
logback-classic     1.2.3
protobuf-java       3.11.3

1. ByteBuffer使用

a迄埃、向 buffer 寫入數據,例如調用 channel.read(buffer)
b兑障、調用 flip() 切換至讀模式
c侄非、從 buffer 讀取數據,例如調用 buffer.get()
d流译、調用 clear() 或 compact() 切換至寫模式
e逞怨、重復 1~4 步驟

try (RandomAccessFile file = new RandomAccessFile("src/main/resources/data.txt", "rw")) {
    FileChannel channel = file.getChannel();
    ByteBuffer buffer = ByteBuffer.allocate(16);
    do {
        //1. 向 buffer 寫入
        int len = channel.read(buffer);
        log.debug("讀到的字節(jié)數:{}", len);
        if (len == -1) {
            break;
        }
        //2. 切換 buffer 讀模式
        buffer.flip();
        while (buffer.hasRemaining()) {
            log.debug("{}", (char) buffer.get());
        }
        //3. 切換 buffer 寫模式
        buffer.clear();
    } while (true);
} catch (IOException e) {
    log.info(e.getMessage());
}

2. ByteBuffer結構

// Creates a new buffer with the given mark, position, limit, capacity,
// backing array, and array offset
ByteBuffer(int mark, int pos, int lim, int cap,   // package-private
             byte[] hb, int offset)
{
    super(mark, pos, lim, cap);
    this.hb = hb;
    this.offset = offset;
}
mark            標記位
position        當前位
limit           界限位
capacity        容量
backing array   支撐數組
array offset    數組偏移

3. ByteBuffer常見方法

① allocate方法

用來給ByteBuffer分配空間

public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw new IllegalArgumentException();
    return new HeapByteBuffer(capacity, capacity);
}

HeapByteBuffer(int cap, int lim) {...} //此時容量對應limit寫上線
allocate方法.png

② channel#read方法 / buffer#put方法

向 buffer 寫入數據

FileChannelImpl#read → IOUtil#readIntoNativeBuffer

public final ByteBuffer put(byte[] src) {
    return put(src, 0, src.length);
}

public ByteBuffer put(byte[] src, int offset, int length) {
    checkBounds(offset, length, src.length);
    if (length > remaining())
        throw new BufferOverflowException();
    int end = offset + length;
    for (int i = offset; i < end; i++)
        this.put(src[i]);
    return this;
}
channel的read方法.png

③ filp方法

切換至【讀模式】,重置position福澡、limit叠赦,可從buffer中讀取數據

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}
filp方法.png

注意:
a、filp方法將 寫limit定位到讀limit革砸,position重置為0除秀,進而讀取內容糯累。
b、另外此時mark也會被清除册踩。

④ hasRemaining方法

判斷是否仍有剩余數據

public final boolean hasRemaining() {
    return position < limit;
}

⑤ buffer#get方法 / channel#write

HeapByteBuffer#get → Buffer#nextGetIndex
FileChannel#write(ByteBuffer[] srcs)

get方法注意點:
a泳姐、會使 position 讀指針向后走;
b暂吉、可以使用 rewind 方法仗岸,使 position 重置,而limit不變借笙,用來重復度扒怖;
c、調用 get(int i) 方法獲取索引 i 的內容业稼,它不會移動讀指針盗痒。

get方法.png
public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

注意:rewind方法會重置mark標記。

對比filp與rewind:后者 rewind 沒有改變 limit指針 所指向的讀上限低散。

⑥ clear方法

public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}
clear方法.png

注意:clear方法并沒有清除內容俯邓,而是改變了指針的指向,提升了效率熔号。

⑦ compact方法

HeapByteBuffer#compact

compact方法.png

注意:compact方法允許我們未讀完稽鞭,而且可以在未讀的后一個位置重新開始寫。

⑧ mark方法 & reset方法

public final Buffer mark() {
    mark = position;
    return this;
}

public final Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    position = m;
    return this;
}
mark方法 & reset方法.png

注意:mark方法與reset方法允許我們在任意mark位置重新讀引镊,rewind方法是從頭開始朦蕴。

⑨ 字符串 與 buffer 互相轉換

ByteBuffer buffer = StandardCharsets.UTF_8.encode("StrToBuffer");
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer);

三、Buffer使用模擬

1. 情景模擬

網絡通信:
a弟头、客戶端發(fā)送多條數據給服務端吩抓,數據間使用"\n"分隔;
b赴恨、數據接收時為了提升效率疹娶,數據會被服務端重新組合。

模擬數據為:
a伦连、Hello, NIO.\n
b雨饺、I`m Stone.\n
c、How are you?\n

此時惑淳,服務器將數據重組额港,出現(xiàn)ByteBuffer (黏包,半包)汛聚,如下:
a锹安、Hello, NIO.\nI`m Stone.\nHo 【24bytes】
c、w are you?\n 【11bytes】

2. 模擬還原數據

省略了buffer動態(tài)擴容與收縮的業(yè)務邏輯,實際使用時叹哭,框架內一般會有代碼實現(xiàn)忍宋。

@Slf4j
public class BufferDemo01 {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        //1. 接收到第一組數據
        //1.1 模擬接收到第一組數據
        buffer.put("Hello, NIO.\nI`m Stone.\nHo".getBytes(StandardCharsets.UTF_8));
        //1.2 處理第一組數據
        split(buffer);
        //2. 接收到第二組數據
        //2.1 模擬接收到第二組數據
        buffer.put("w are you?\n".getBytes(StandardCharsets.UTF_8));
        //2.2 處理第二組數據
        split(buffer);
    }

    public static void split(ByteBuffer buffer) {
        //1. 切換至 讀模式
        buffer.flip();
        //2. 記錄當前 讀上限
        int originLimit = buffer.limit();
        //3. 處理當前數據
        for (int i = 0; i < originLimit; i++) {
            //3.1 如果讀取到的數據是規(guī)定的 分隔符"\n"
            if (buffer.get(i) == '\n') {
                log.debug("當前分隔符所在的位置:{},buffer.position():{}风罩。", i, buffer.position());
                ByteBuffer message = ByteBuffer.allocate(i + 1 - buffer.position());
                buffer.limit(i + 1); //3.2 調整當前讀上限為 message 容量
                message.put(buffer); //3.3 從 buffer 讀糠排,向 message 寫
                //debugAll(message); //該方法是打印當前 message 的方法
                buffer.limit(originLimit); //3.4 調整當前讀上限為原先讀originLimit
            }
        }
        //4. 如果當前數據有剩余,則將當前數據拼接至下組數據
        buffer.compact();
    }
}
輸出內容.png

四超升、結尾

以上即為Netty基礎-NIO(一)的全部內容入宦,感謝閱讀。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末室琢,一起剝皮案震驚了整個濱河市乾闰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盈滴,老刑警劉巖涯肩,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巢钓,居然都是意外死亡病苗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門症汹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硫朦,“玉大人,你說我怎么就攤上這事背镇∫д梗” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵芽世,是天一觀的道長挚赊。 經常有香客問我,道長济瓢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任妹卿,我火速辦了婚禮旺矾,結果婚禮上,老公的妹妹穿的比我還像新娘夺克。我一直安慰自己箕宙,他們只是感情好,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布铺纽。 她就那樣靜靜地躺著柬帕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陷寝,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天锅很,我揣著相機與錄音,去河邊找鬼凤跑。 笑死爆安,一個胖子當著我的面吹牛,可吹牛的內容都是我干的仔引。 我是一名探鬼主播扔仓,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咖耘!你這毒婦竟也來了翘簇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤儿倒,失蹤者是張志新(化名)和其女友劉穎缘揪,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體义桂,經...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡找筝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了慷吊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袖裕。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溉瓶,靈堂內的尸體忽然破棺而出急鳄,到底是詐尸還是另有隱情,我是刑警寧澤堰酿,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布疾宏,位于F島的核電站,受9級特大地震影響触创,放射性物質發(fā)生泄漏坎藐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一哼绑、第九天 我趴在偏房一處隱蔽的房頂上張望岩馍。 院中可真熱鬧,春花似錦抖韩、人聲如沸蛀恩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽双谆。三九已至壳咕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間顽馋,已是汗流浹背谓厘。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趣避,地道東北人庞呕。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像程帕,于是被迫代替她去往敵國和親住练。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容