1. 程式人生 > >Netty原始碼分析-- 處理客戶端接入請求(八)

Netty原始碼分析-- 處理客戶端接入請求(八)

           這一節我們來一起看下,一個客戶端接入進來是什麼情況。首先我們根據之前的分析,先啟動服務端,然後打一個斷點。

    這個斷點打在哪裡呢?就是NioEventLoop上的select方法上。

    

   然後我們啟動一個客戶端。

   

  然後我們debug看到,selectedKey的數量 = 1,說明有accept或者讀寫等事件發生。

  接下就會進 processSelectedKeys() 

我們上一節講到,這裡的attach就是NioServerSocketChannel, 我們進入 processSelectedKey() 方法

     重點來了,這裡對各種事件進行分發,debug看我們現在是16,也就是說是accept事件。

     繼續跟進去F5進去

     

    發現進入到了AbstractNioMessageChannel的read方法。這裡進入的是 AbstractNioMessageChannel !!!;

    為了加深印象,我們先暫停,重新跑一個讀事件

   

  發現當前的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註冊(上)(五)

   Netty原始碼分析--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的記憶體模型是什麼樣子的。

 &n