這一節咱們來一塊兒看下,一個客戶端接入進來是什麼狀況。首先咱們根據以前的分析,先啓動服務端,而後打一個斷點。html
這個斷點打在哪裏呢?就是NioEventLoop上的select方法上。socket
而後咱們啓動一個客戶端。工具
而後咱們debug看到,selectedKey的數量 = 1,說明有accept或者讀寫等事件發生。oop
接下就會進 processSelectedKeys() 源碼分析
咱們上一節講到,這裏的attach就是NioServerSocketChannel, 咱們進入 processSelectedKey() 方法post
重點來了,這裏對各類事件進行分發,debug看咱們如今是16,也就是說是accept事件。url
繼續跟進去F5進去spa
發現進入到了AbstractNioMessageChannel的read方法。這裏進入的是 AbstractNioMessageChannel !!!;debug
爲了加深印象,咱們先暫停,從新跑一個讀事件3d
發現當前的selectKey = 1,也就是讀事件
F5 進入
發現,讀事件進入到AbstractNioByteChannel中,
那麼也就是accept進入 AbstractNioMessageChannel , 而 read 進入AbstractNioByteChannel 中。
好了,咱們回過頭來繼續看 AbstractNioMessageChannel 中的read 方法。
關於這個分配器,這裏我先不看,待後面分析內存模型的時候再說。咱們直接看下面的代碼:
咱們發現這個是一個循環,不斷的調用doReadMessages方法,而且傳入了一個readBuf,並且這個readBuf是一個ArrayList, 那這裏咱們猜想多是把讀取到的客戶端放到List集合中保存,而後再循環處理客戶端鏈接。
進入doReadMessage方法一探究竟。
經過工具類調用 NioServerSocketChannel 內部封裝的 serverSocketChannel 的 accept 方法,獲取一個SocketChannel, 若是你們還記得我第一篇講NIO的地方 Netty源碼分析--NIO(一),這裏應該會有印象,我這裏貼出來:
這裏就是獲取到了客戶端的socketChannel
而後這裏將SocketChannel封裝成了一個NioSocketChannel,而後添加到了readBuf這個ArrayList中存儲。
封裝這裏,這裏不想多說了,跟建立NioServerSocketChannel相似,你們能夠去回顧一下 Netty源碼分析--建立Channel(三)。
繼續往下看,這裏就是循環readBuf,鏈式執行 管道中的 handler 的 ChannelRead 方法。
根據前面幾篇的分析,咱們知道 , pipeline 裏面又 4 個 handler ,分別是 Head,LoggingHandler,ServerBootstrapAcceptor,Tail,鏈式調用其中的 ChannelRead 方法,這裏咱們着重看 ServerBootstrapAcceptor 中個的 ChannelRead 方法。
這裏爲剛剛的客戶端channel 添加了handler,設置了options和childAttrs,注意這裏的addLast方法,並無調用initChannel方法,具體這裏添加了什麼,我前面幾篇有說起,你們能夠再回顧一下。
接下來這裏呢,就是把客戶端channel註冊到多路複用器上,跟服務端channel註冊的流程是同樣的。你們能夠去看
Netty源碼分析--Channel註冊&綁定端口(下)(七)
咱們直接說重點:
註冊完成以後,就進入到了 pipeline.invokeHandlerAddedIfNeeded() 方法,咱們跟下這個代碼。
跟下去咱們會進入上圖這個execute()方法,如上圖,咱們看下 ctx
那麼鏈式結構也就是 HeadContext -> NettyServer【ChannelInitializer】(個人啓動類) -> TailContext
不斷的跟下去,咱們發現其實就是去調用當初咱們在NettyServer中的initChannel。
那麼也就是說,這裏纔是真正往pipeline中添加handler的過程!!!
神奇的是,後面還有一個remove方法。
那麼這個是啥意思呢?咱們再來看下ctx的鏈式結構
也就是說變成了 HeadContext - > NettyServer -> IdleStateHandler ...等 -> TailContext
你們發現了嗎? NettyServer 還在, 也就是 ChannelInitializer 這個handler 還在鏈上,可是它的做用已經結束了,沒錯,這裏刪除的就是它。
怎麼刪的就不說了,無非就是把 ChannelInitializer 兩段的鏈表直接鏈接起來,把 ChannelInitializer 剔除就能夠了。
接下來就是 pipeline.fireChannelRegistered(); 和 pipeline.fireChannelActive();
就是在全部的handler中鏈式調用channelRegister 和 channelActive方法。
總結一下:ServerBootstrapAcceptor 纔是那個負責接收客戶端鏈接,而且將其註冊到多路複用器上的核心類
那麼到這裏,客戶端的接入就完成了,下一篇咱們來看,客戶端的讀寫過程以及Netty的內存模型是什麼樣子的。