最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

《跟閃電俠學(xué)Netty》閱讀筆記 - Netty入門程序解析

2023-08-24 14:41 作者:懶時小窩  | 我要投稿

Part1引言

上一節(jié)[《跟閃電俠學(xué)Netty》閱讀筆記 - 開篇入門Netty] 中介紹了Netty的入門程序,本節(jié)如標題所言將會一步步分析入門程序的代碼含義。

Part2思維導(dǎo)圖

《跟閃電俠學(xué)Netty》 - Netty入門程序解析(二).png

Part3服務(wù)端最簡化代碼

?public?static?void?main(String[]?args)?{
????????ServerBootstrap?serverBootstrap?=?new?ServerBootstrap();

????????NioEventLoopGroup?boos?=?new?NioEventLoopGroup();
????????NioEventLoopGroup?worker?=?new?NioEventLoopGroup();
????????serverBootstrap
???.group(boos,?worker)
???.channel(NioServerSocketChannel.class)
???.childHandler(new?ChannelInitializer<NioSocketChannel>()?
{
????protected?void?initChannel(NioSocketChannel?ch)?{
?????ch.pipeline().addLast(new?StringDecoder());
?????ch.pipeline().addLast(new?SimpleChannelInboundHandler<String>()?{
??????@Override
??????protected?void?channelRead0(ChannelHandlerContext?ctx,?String?msg)?{
???????System.out.println(msg);
??????}
?????});
????}
???})
???.bind(8000);
????}

1兩個NioEventLoopGroup

服務(wù)端一上來先構(gòu)建兩個對象NioEventLoopGroup,這兩個對象將直接決定Netty啟動之后的工作模式,在這個案例中boos和JDK的NIO編程一樣負責(zé)進行新連接的“輪詢”,他會定期檢查客戶端是否已經(jīng)準備好可以接入。worker則負責(zé)處理boss獲取到的連接,當檢查連接有數(shù)據(jù)可以讀寫的時候就進行數(shù)據(jù)處理。

NioEventLoopGroup?boos?=?new?NioEventLoopGroup();
NioEventLoopGroup?worker?=?new?NioEventLoopGroup();

那么應(yīng)該如何理解?其實這兩個Group對象簡單的看成是線程池即可,和JDBC的線程池沒什么區(qū)別。通過閱讀源碼可以知道,bossGroup只用了一個線程來處理遠程客戶端的連接,workerGroup 擁有的線程數(shù)默認為2倍的cpu核心數(shù)

那么這兩個線程池是如何配合的?boss和worker的工作模式和我們平時上班,老板接活員工干活的模式是類似的。bossGroup負責(zé)接待,再轉(zhuǎn)交給workerGroup來處理具體的業(yè)務(wù)。

整體概念上貼合NIO的設(shè)計思路,不過它要做的更好。

2ServerBootstrap

ServerBootstrap?serverBootstrap?=?new?ServerBootstrap();
serverBootstrap.
?.xxx()
?.xxx()

服務(wù)端引導(dǎo)類是ServerBootstrap,引導(dǎo)器指的是引導(dǎo)開發(fā)者更方便快速的啟動Netty服務(wù)端/客戶端,

這里使用了比較經(jīng)典的建造者設(shè)計模式。

3group設(shè)置

.group(boos,?worker)

group方法綁定boos和work使其各司其職,這個操作可以看作是綁定線程池。

注意gorup方法一旦確定就意味著Netty的線程模型被固定了,中途不允許切換,整個運行過程Netty會按照代碼實現(xiàn)計算的線程數(shù)提供服務(wù)。

下面是group的api注釋:

Set the EventLoopGroup for the parent (acceptor) and the child (client). These EventLoopGroup's are used to handle all the events and IO for ServerChannel and Channel's.

機翻過來就是:為父(acceptor)和子(client)設(shè)置EventLoopGroup。這些EventLoopGroup是用來處理ServerChannelChannel的所有事件和IO的。注意這里的 Channel's 是Netty中的概念,初學(xué)的時候可以簡單的類比為BIO編程的Socket套接字。

4channel

.channel(NioServerSocketChannel.class)

設(shè)置底層編程模型或者說底層通信模式,一旦設(shè)置中途不允許更改。所謂的底層編程模型,其實就是JDK的BIO,NIO模型(Netty擯棄了JDK的AIO編程模型),除此之外Netty還提供了自己編寫的Epoll模型,當然日常工作中是用最多的還是NIO模型。

5childHandler

childHandler方法主要作用是初始化和定義處理鏈來處理請求處理的細節(jié)。在案例代碼當中我們添加了Netty提供的字符串解碼handler(StringDecoder)和由Netty實現(xiàn)的SimpleChannelInboundHandler簡易腳手架,腳手架中自定義的處理邏輯為打印客戶端發(fā)送的請求數(shù)據(jù)。

.childHandler(new?ChannelInitializer<NioSocketChannel>()?{
????protected?void?initChannel(NioSocketChannel?ch)?{
?????ch.pipeline().addLast(new?StringDecoder());
?????ch.pipeline().addLast(new?SimpleChannelInboundHandler<String>()?{
??????@Override
??????protected?void?channelRead0(ChannelHandlerContext?ctx,?String?msg)?{
???????System.out.println(msg);
??????}
?????});
????}
???})

Handler負責(zé)處理一個I/O事件或攔截一個I/O操作,處理完成將其轉(zhuǎn)發(fā)給其ChannelPipeline中的下一個處理Handler,以此形成經(jīng)典的處理鏈條。 比如案例里面StringDecoder解碼處理數(shù)據(jù)之后將會交給SimpleChannelInboundHandlerchannelRead0方法,該方法中將解碼讀取到的數(shù)據(jù)打印到控制臺。

借助pipeline,我們可以定義連接收到請求后續(xù)的數(shù)據(jù)讀寫細節(jié)和處理邏輯。為了方便理解,這里可以認為NIoSocketChanne 對應(yīng)BIO編程模型的Socket套接字 ,NioServerSocketChannel對應(yīng)BIO編程模型的ServerSocket

6bind

.bind(8000)

bind操作是一個異步方法,它會返回ChannelFuture,服務(wù)端編碼中可以通過添加監(jiān)聽器方式,自定義在Netty服務(wù)端啟動回調(diào)通知之后的下一步處理邏輯,當然也可以完全不關(guān)心它是否啟動繼續(xù)往下執(zhí)行其他業(yè)務(wù)代碼的處理。

Netty的 ChannelFuture 類注釋中有一個簡單直觀的例子介紹ChannelFuture的使用。

//?GOOD
??Bootstrap?b?=?...;
??//?Configure?the?connect?timeout?option.
??b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,?10000);
??ChannelFuture?f?=?b.connect(...);
??f.awaitUninterruptibly();
?
??//?Now?we?are?sure?the?future?is?completed.
??assert?f.isDone();
?
??if?(f.isCancelled())?{
??????//?Connection?attempt?cancelled?by?user
??}?else?if?(!f.isSuccess())?{
??????f.cause().printStackTrace();
??}?else?{
??????//?Connection?established?successfully
??}

這個過程類似外面員把外賣送到指定地點之后打電話通知我們。

Part4實踐:服務(wù)端啟動失敗自動遞增端口號重新綁定端口

第一個案例是通過服務(wù)端啟動失敗自動遞增端口號重新綁定端口。

需求

服務(wù)端啟動必須要關(guān)心的問題是指定的端口被占用導(dǎo)致啟動失敗的處理,這里的代碼實踐是利用Netty的API完成服務(wù)端端口在檢測到端口被占用的時候自動+1重試綁定直到所有的端口耗盡。

思路

實現(xiàn)代碼如下:

??
public?class?NettyServerStart?{??
??
????public?static?void?main(String[]?args)?{??
????????ServerBootstrap?serverBootstrap?=?new?ServerBootstrap();??
??
????????NioEventLoopGroup?boss?=?new?NioEventLoopGroup();??
????????NioEventLoopGroup?worker?=?new?NioEventLoopGroup();??
????????int?port?=?10022;??
????????serverBootstrap??
????????????????.group(boss,?worker)??
????????????????.channel(NioServerSocketChannel.class)??
????????????????.handler(new?ChannelInitializer()?
{??
????????????????????@Override??
????????????????????protected?void?initChannel(Channel?ch)?throws?Exception?{??
????????????????????????//?指定服務(wù)端啟動過程的一些邏輯??
????????????????????????System.out.println("服務(wù)端啟動當中");??
????????????????????}??
????????????????})??
??
????????????????//?指定自定義屬性,客戶端可以根據(jù)此屬性進行一些判斷處理??
????????????????//?可以看作給Channel維護一個Map屬性,這里的channel是服務(wù)端??
????????????????//?允許指定一個新創(chuàng)建的通道的初始屬性。如果該值為空,指定鍵的屬性將被刪除。??
????????????????.attr(AttributeKey.newInstance("hello"),?"hello?world")??
??
????????????????//?給每個連接指定自定義屬性,Channel?進行屬性指定等??
????????????????//?用給定的值在每個?子通道?上設(shè)置特定的AttributeKey。如果該值為空,則AttributeKey將被刪除。??
????????????????//?區(qū)別是是否是?子channel,子Channel代表給客戶端的連接設(shè)置??
????????????????.childAttr(AttributeKey.newInstance("childAttr"),?"childAttr")??
??
????????????????//?客戶端的?Channel?設(shè)置TCP?參數(shù)??
????????????????//?so_backlog?臨時存放已完成三次握手的請求隊列的最大長度,如果頻繁連接可以調(diào)大此參數(shù)??
????????????????.option(ChannelOption.SO_BACKLOG,?1024)??
??
????????????????//?給每個連接設(shè)置TCP參數(shù)??
????????????????//?tcp的心跳檢測,true為開啟??
????????????????.childOption(ChannelOption.SO_KEEPALIVE,?true)??
????????????????//?nagle?算法開關(guān),實時性要求高就關(guān)閉??
????????????????.childOption(ChannelOption.TCP_NODELAY,?true)??
??
????????????????.childHandler(new?ChannelInitializer<NioSocketChannel>()?{??
????????????????????@Override??
????????????????????protected?void?initChannel(NioSocketChannel?ch)?throws?Exception?{??
????????????????????????ch.pipeline().addLast(new?StringDecoder());??
????????????????????????ch.pipeline().addLast(new?SimpleChannelInboundHandler<String>()?{??
????????????????????????????@Override??
????????????????????????????protected?void?channelRead0(ChannelHandlerContext?ctx,?String?msg)?throws?Exception?{??
????????????????????????????????System.err.println(msg);??
????????????????????????????}??
????????????????????????});??
??
????????????????????}??
??
????????????????});??
????????bind(serverBootstrap,?port);??
????}??
??
????/**??
?????*?自動綁定遞增端口??
?????*?@param?serverBootstrap??
?????*?@param?port??
?????*/
??
????public?static?void?bind(ServerBootstrap?serverBootstrap,?int?port){??
????????serverBootstrap.bind(port).addListener(future?->?{??
????????????if(future.isSuccess()){??
????????????????System.out.println("端口綁定成功");??
????????????????System.out.println("綁定端口"+?port?+"成功");??
????????????}else{??
????????????????System.out.println("端口綁定失敗");??
????????????????bind(serverBootstrap,?port+1);??
????????????}??
????????});??
????}??
}

Part5服務(wù)端API其他方法

詳細介紹和解釋API個人認為意義不大,這里僅僅對于常用的API進行解釋:

  • handler():代表服務(wù)端啟動過程當中的邏輯,服務(wù)端啟動代碼中基本很少使用。

  • childHandler():用于指定每個新連接數(shù)據(jù)的讀寫處理邏輯,類似流水線上安排每一道工序的處理細節(jié)。

  • attr():底層實際上就是一個Map,用戶可以為服務(wù)端Channel指定屬性,可以通過自定義屬性實現(xiàn)一些特殊業(yè)務(wù)。(不推薦這樣做,會導(dǎo)致業(yè)務(wù)代碼和Netty高度耦合)

  • childAttr():為每一個連接指定屬性,可以使用channel.attr()取出屬性。

  • option():可以為Channel配置TCP參數(shù)。

    • so_backlog:表示臨時存放三次握手請求隊列(syns_queue:半連接隊列)的最大容量,如果連接頻繁處理新連接變慢,適當擴大此參數(shù)。這個參數(shù)的主要作用是預(yù)防“DOS”攻擊占用。

  • childOption():為每個連接設(shè)置TCP參數(shù)。

    • TCP_NODELAY:是否開啟Nagle算法,如果需要減少網(wǎng)絡(luò)交互次數(shù)建議開啟,要求高實時性建議關(guān)閉。

    • SO_KEEPALIVE:TCP底層心跳機制。

Part6客戶端最簡化代碼

客戶端的啟動代碼如下。


public?static?void?main(String[]?args)?throws?InterruptedException?{??
????Bootstrap?bootstrap?=?new?Bootstrap();??
????NioEventLoopGroup?eventExecutors?=?new?NioEventLoopGroup();??
????//?引導(dǎo)器引導(dǎo)啟動??
????bootstrap.group(eventExecutors)??
????????????.channel(NioSocketChannel.class)??
????????????.handler(new?ChannelInitializer<Channel>()?
{??
????????????????@Override??
????????????????protected?void?initChannel(Channel?channel)?throws?Exception?{??
????????????????????channel.pipeline().addLast(new?StringEncoder());??
????????????????}??
????????????});??
??
????//?建立通道??
????Channel?channel?=?bootstrap.connect("127.0.0.1",?8000).channel();??
??
????while?(true){??
????????channel.writeAndFlush(new?Date()?+?"?Hello?world");??
????????Thread.sleep(2000);??
????}??
??
}

客戶端代碼最主要的三個關(guān)注點是:線程模型、IO模型、IO業(yè)務(wù)處理邏輯,其他代碼和服務(wù)端的啟動比較類似。這里依舊是從上往下一條條分析代碼。

7Bootstrap

客戶端連接不需要監(jiān)聽端口,為了和服務(wù)端區(qū)分直接被叫做Bootstrap,代表客戶端的啟動引導(dǎo)器。

Bootstrap?bootstrap?=?new?Bootstrap();??

8NioEventLoopGroup

Netty中客戶端也同樣需要設(shè)置線程模型才能和服務(wù)端正確交互,客戶端的NioEventLoopGroup同樣可以看作是線程池,負責(zé)和服務(wù)端的數(shù)據(jù)讀寫處理。

NioEventLoopGroup?eventExecutors?=?new?NioEventLoopGroup();??

9group

客戶端 group 線程池的設(shè)置只需要一個即可,因為主要目的是和服務(wù)端建立連接(只需要一個線程即可)。

.group(eventExecutors)

10channel

和服務(wù)端設(shè)置同理,作用是底層編程模型的設(shè)置。官方注釋中推薦使用NIO / EPOLL / KQUEUE這幾種,使用最多的是NIO。

.channel(NioSocketChannel.class)

這里比較好奇如果用OIO模型的客戶端連接NIO的服務(wù)端會怎么樣? 于是做了個實驗,把如下代碼改為OioServerSocketChannel(生產(chǎn)禁止使用,此方式已被Deprecated),啟動服務(wù)端之后啟動客戶端即可觀察效果。

.channel(OioServerSocketChannel.class)

從實驗結(jié)果來看,顯然不允許這么干。

15:24:00.934?[main]?WARN??io.netty.bootstrap.Bootstrap?-?Unknown?channel?option?'SO_KEEPALIVE'?for?channel?'[id:?0xd0aaab57]'
15:24:00.934?[main]?WARN??io.netty.bootstrap.Bootstrap?-?Unknown?channel?option?'TCP_NODELAY'?for?channel?'[id:?0xd0aaab57]'

11handler

上文介紹服務(wù)端的時候提到過 handler()代表服務(wù)端啟動過程當中的邏輯,在這里自然就表示客戶端啟動過程的邏輯,客戶端的handler()可以直接看作服務(wù)端引導(dǎo)器當中的childHandler()。

這里讀者可能會好奇為什么客戶端代碼用childHandler呢?答案是Netty為了防止使用者誤解Bootstrap中只有handler,所以我們可以直接等同于服務(wù)端的childHandler()。

吐槽:這個child不child的API名稱看的比較蛋疼,不加以區(qū)分有時候確實容易用錯。這里生活化理解服務(wù)端中的childHandler是身上帶了連接,所以在連接成功之后會調(diào)用,沒有child則代表此時沒有任何連接,所以會發(fā)送在初始化的時候調(diào)用。

而客戶端為什么只保留 handler() 呢?個人理解是客戶端最關(guān)注的是連接上服務(wù)端之后所做的處理,增加初始化的時候做處理沒啥意義,并且會導(dǎo)致設(shè)計變復(fù)雜。

handler內(nèi)部是對于Channel進行初始化并且添加pipline自定義客戶端的讀寫邏輯。這里同樣添加Netty提供的StringEncoder默認會是用字符串編碼模式對于發(fā)送的數(shù)據(jù)進行編碼處理。

channel.pipeline().addLast(new?StringEncoder());??

ChannelInitializer可以直接類比SocketChannel。

12connect

當配置都準備好之后,客戶端的最后一步是啟動客戶端并且和服務(wù)端進行TCP三次握手建立連接。這里方法會返回Channel對象,Netty的connect支持異步連接。

Channel?channel?=?bootstrap.connect("127.0.0.1",?8000).channel();??

再次強調(diào),connect 是一個異步方法,同樣可以通過給返回的channel對象調(diào)用addListner添加監(jiān)聽器,在Netty的客戶端成功和服務(wù)端建立連接之后會回調(diào)相關(guān)方法告知監(jiān)聽器所有數(shù)據(jù)準備完成,此時可以在監(jiān)聽器中添加回調(diào)之后的處理邏輯。

我們還可以用監(jiān)聽器對于連接失敗的情況做自定義處理邏輯,比如下面例子將會介紹利用監(jiān)聽器實現(xiàn)客戶端連接服務(wù)端失敗之后,定時自動重連服務(wù)端多次直到重連次數(shù)用完的例子。

Part7實踐:客戶端失敗重連

第二個實踐代碼是客戶端在連接服務(wù)端的時候進行失敗重連。失敗重連在網(wǎng)絡(luò)環(huán)境較差的時候十分有效,但是需要注意這里的代碼中多次重試會逐漸增加時間間隔。

客戶端失敗重連的整體代碼如下:

private?static?void?connect(Bootstrap?bootstrap,?String?host,?int?port,?int?retry)?{??
????bootstrap.connect(host,?port).addListener(future?->?{??
????????if?(future.isSuccess())?{??
????????????System.out.println(new?Date()?+?":?連接成功,啟動控制臺線程……");??
????????????Channel?channel?=?((ChannelFuture)?future).channel();??
????????????startConsoleThread(channel);??
????????}?else?if?(retry?==?0)?{??
????????????System.err.println("重試次數(shù)已用完,放棄連接!");??
????????}?else?{??
????????????//?第幾次重連??
????????????int?order?=?(MAX_RETRY?-?retry)?+?1;??
????????????//?本次重連的間隔??
????????????int?delay?=?1?<<?order;??
????????????System.err.println(new?Date()?+?":?連接失敗,第"?+?order?+?"次重連……");??
????????????bootstrap.config().group().schedule(()?->?connect(bootstrap,?host,?port,?retry?-?1),?delay,?TimeUnit??
????????????????????.SECONDS);??
????????}??
????});??
}

private?static?void?startConsoleThread(Channel?channel)?{??
????ConsoleCommandManager?consoleCommandManager?=?new?ConsoleCommandManager();??
????LoginConsoleCommand?loginConsoleCommand?=?new?LoginConsoleCommand();??
????Scanner?scanner?=?new?Scanner(System.in);??
??
????new?Thread(()?->?{??
????????while?(!Thread.interrupted())?{??
????????????if?(!SessionUtil.hasLogin(channel))?{??
????????????????loginConsoleCommand.exec(scanner,?channel);??
????????????}?else?{??
????????????????consoleCommandManager.exec(scanner,?channel);??
????????????}??
????????}??
????}).start();??
}

加入失敗重連代碼之后,客戶端的啟動代碼需要進行略微調(diào)整,在鏈式調(diào)用中不再使用直接connection,而是傳遞引導(dǎo)類和相關(guān)參數(shù),通過遞歸的方式實現(xiàn)失敗重連的效果:

connect(bootstrap,?"127.0.0.1",?10999,?MAX_RETRY);

Part8客戶端API其他方法和相關(guān)屬性

13attr()

  • NioChannel綁定自定義屬性

  • 底層實際為Map

  • NioSocketChannel存儲參數(shù)使用此方法取出

14三種TCP關(guān)聯(lián)參數(shù)

SO_KEEPALIVE

對應(yīng)源碼定義如下:

public?static?final?ChannelOption<Boolean>?SO_KEEPALIVE?=?valueOf("SO_KEEPALIVE");

主要關(guān)聯(lián)TCP底層心跳機制。SO_KEEPALIVE用于開啟或者關(guān)閉保活探測,默認情況下是關(guān)閉的。當SO_KEEPALIVE開啟時,可以保持連接檢測對方主機是否崩潰,避免(服務(wù)器)永遠阻塞于TCP連接的輸入。

TCP_NODELAY

Nagle 算法解釋

這個參數(shù)的含義是:是否開啟Nagle算法。首先需要注意這個參數(shù)和Linux操作系統(tǒng)的默認值不一樣,true 傳輸?shù)絃inux是關(guān)閉調(diào)Nagle算法。

Nagele算法的出現(xiàn)和以前的網(wǎng)絡(luò)帶寬資源有限有關(guān),為了盡可能的利用網(wǎng)絡(luò)帶寬,TCP總是希望盡可能的發(fā)送足夠大的數(shù)據(jù),Nagle算法就是為了盡可能發(fā)送大塊數(shù)據(jù),避免網(wǎng)絡(luò)中充斥著許多小數(shù)據(jù)塊

為了理解Nagle算法,我們需要了解TCP的緩沖區(qū)通常會設(shè)置 MSS 參數(shù)。

MSS 參數(shù):除去 IP 和 TCP 頭部之后,一個網(wǎng)絡(luò)包所能容納的 TCP 數(shù)據(jù)的最大長度; 最大 1460。MTU:一個網(wǎng)絡(luò)包的最大長度,以太網(wǎng)中一般為 1500 字節(jié);

為什么最大為1460個字節(jié)? 因為TCP傳輸過程中都會要求綁定 TCP 和 IP 的頭部信息,這樣服務(wù)端才能回送ACK確認收到包正確。

image.png

也就是說傳輸大數(shù)據(jù)包的時候,數(shù)據(jù)會按照MSS的值進行切割。回到Nagle算法,它的作用就是定義任意時刻只能有一個未被ACK確認的小段(MSS對應(yīng)切割的一個塊)。

這就意味著當有多個未被ACK確認的小段的時候,此時client端會小小的延遲一下等待合并為更大的數(shù)據(jù)包才發(fā)送。

Netty 默認關(guān)閉了這個算法,意味著一有數(shù)據(jù)就理解發(fā)送,滿足低延遲和高并發(fā)的設(shè)計。

Netty源碼關(guān)聯(lián)

TCP_NODELAY 配置選項定義如下:

public?static?final?ChannelOption<Boolean>?TCP_NODELAY?=?valueOf("TCP_NODELAY");

此參數(shù)的配置介紹可以從 SocketChannelConfig 關(guān)聯(lián)的配置中獲取。

/**??
?*?Gets?the?{@link?StandardSocketOptions#TCP_NODELAY}?option.??Please?note?that?the?default?value?of?this?option??
?*?is?{@code?true}?unlike?the?operating?system?default?({@code?false}).?However,?for?some?buggy?platforms,?such?as??
?*?Android,?that?shows?erratic?behavior?with?Nagle's?algorithm?disabled,?the?default?value?remains?to?be?*?{@code?false}.??
?*/

?boolean?isTcpNoDelay();

注釋翻譯如下。

獲取 StandardSocketOptions.TCP_NODELAY 配置。請注意,該選項的默認值為 true,與操作系統(tǒng)的默認值(false)不同。然而,對于一些有問題的平臺,比如Android,在禁用Nagle算法的情況下會出現(xiàn)不穩(wěn)定的行為,默認值仍然為false。

CONNECTION_TIMEOUT

表示連接超時時間,單位為毫秒。

Part9客戶端和服務(wù)端通信

本部分可以參考作者代碼,這里僅僅用筆記歸檔一下大致代碼編寫思路。

https://github.com/lightningMan/flash-netty

15客戶端寫入數(shù)據(jù)到服務(wù)端

  • handler 方法:指定客戶端通信處理邏輯

  • initChannel 方法:給客戶端添加邏輯處理器

  • pipeline:邏輯處理鏈添加邏輯處理器

    • 覆蓋channelActive()方法

    • 客戶端連接建立成功提示打印

    • addLast 添加自定義ChannelHandler

    • 邏輯處理器繼承自ChannelHandler

    • 邏輯處理器可以通過繼承適配類ChannelInboundHandlerAdapter實現(xiàn)簡化開發(fā)

  • 寫數(shù)據(jù)部分ByteBuf (Netty實現(xiàn))




    1. writeAndFlush 刷緩存

    1. byte[] 數(shù)據(jù)填充二進制數(shù)據(jù)

    1. alloc獲得內(nèi)存管理器

16服務(wù)端讀取客戶端數(shù)據(jù)

  • 邏輯處理器繼承適配類

    • 邏輯處理器可以通過繼承適配類ChannelInboundHandlerAdapter實現(xiàn)簡化開發(fā)

  • 接收數(shù)據(jù)和服務(wù)端讀取數(shù)據(jù)類似

  • 構(gòu)建ByteBuf




    1. writeAndFlush 刷緩存

    1. byte[] 數(shù)據(jù)填充二進制數(shù)據(jù)

    1. alloc獲得內(nèi)存管理器

  • 通過writeAndFlush寫出數(shù)據(jù)給客戶端

17服務(wù)端返回數(shù)據(jù)給客戶端

  • 邏輯處理器繼承適配類

    • 邏輯處理器可以通過繼承適配類ChannelInboundHandlerAdapter實現(xiàn)簡化開發(fā)

  • 接收數(shù)據(jù)和服務(wù)端讀取數(shù)據(jù)類似

  • 構(gòu)建ByteBuf




    1. writeAndFlush 刷緩存

    1. byte[] 數(shù)據(jù)填充二進制數(shù)據(jù)

    1. alloc獲得內(nèi)存管理器

  • 通過writeAndFlush寫出數(shù)據(jù)給客戶端

18客戶端讀取服務(wù)端數(shù)據(jù)

  • 和服務(wù)端讀取客戶端數(shù)據(jù)思路類似

  • 關(guān)鍵是需要覆蓋channelRead() 方法

19核心概念

  • Netty當中,childHanlder 和 handler 對應(yīng)客戶端服務(wù)端處理邏輯

  • ByteBuf 數(shù)據(jù)載體,類似隧道兩端的中間小推車。JDK官方實現(xiàn)java.nio.ByteBuffer存在各種問題,Netty官方重新實現(xiàn)了io.netty.buffer.ByteBuf。

  • 服務(wù)端讀取對象基本單位為Object,如果需要讀取其他對象類型通常需要強轉(zhuǎn)。

  • 邏輯處理器都可以通過繼承適配器實現(xiàn),客戶端和服務(wù)端覆蓋對應(yīng)方法實現(xiàn)被動接收或者主動推送。

Part10章節(jié)末尾問題

20客戶端API對比服務(wù)端少了什么內(nèi)容?

  1. “group”。

  2. 客戶端只有childHandler,

21新連接接入時候,如何實現(xiàn)服務(wù)端主動推送消息,然后客戶端進行回復(fù)?

答案是添加監(jiān)聽器,在監(jiān)聽到客戶端連接成功之后直接主動推送自定義信息。

22handler()和childHandler()有什么區(qū)別

初學(xué)者比較容易困擾的問題。handler()childHandler()的主要區(qū)別是:handler()是發(fā)生在初始化的時候childHandler()是發(fā)生在客戶端連接之后。

“知其所以然”的部分放到后續(xù)的源碼分析筆記當中,這里暫時跳過,初次閱讀只需要記住結(jié)論即可。

Part11八股

23BIO的Socket和NIO的SocketChannel 區(qū)別?

本質(zhì)上都是客戶端和服務(wù)端進行網(wǎng)絡(luò)通信的連接的一種抽象,但是使用上有不小的區(qū)別。下面的內(nèi)容摘錄自參考資料:

Socket、SocketChannel區(qū)別: https://blog.csdn.net/A350204530/article/details/78606298 Netty Channel的理解: https://segmentfault.com/q/1010000011974154


《跟閃電俠學(xué)Netty》閱讀筆記 - Netty入門程序解析的評論 (共 條)

分享到微博請遵守國家法律
昌邑市| 启东市| 虞城县| 运城市| 合阳县| 芒康县| 克什克腾旗| 丁青县| 凤凰县| 天门市| 蕲春县| 崇信县| 岳阳市| 丹巴县| 东方市| 新邵县| 洞头县| 金沙县| 朝阳县| 凌源市| 枝江市| 常宁市| 汽车| 昭平县| 柏乡县| 台州市| 丰城市| 银川市| 崇信县| 巴塘县| 巨野县| 南京市| 凭祥市| 克什克腾旗| 根河市| 大石桥市| 金川县| 同德县| 正安县| 湾仔区| 乌什县|