下面,我們對nio服務端的主要建立過程進行講解和說明,作為nio的基礎入門,我們将忽略掉一些在生産環境中部署所需要的一些特性和功能。
步驟一:打開serversocketchannel,用于監聽用戶端的連接配接,它是所有用戶端連接配接的父管道,代碼示例如下:
<code>1</code>
<code>serversocketchannel acceptorsvr = serversocketchannel.open();</code>
步驟二:綁定監聽端口,設定連接配接為非阻塞模式,示例代碼如下:
<code>acceptorsvr.socket().bind(</code><code>new</code> <code>inetsocketaddress(inetaddress.getbyname(“ip”), port));</code>
<code>2</code>
<code>acceptorsvr.configureblocking(</code><code>false</code><code>);</code>
步驟三:建立reactor線程,建立多路複用器并啟動線程,代碼如下:
<code>selector selector = selector.open();</code>
<code>new thread(</code><code>new</code> <code>reactortask()).start();</code>
步驟四:将serversocketchannel注冊到reactor線程的多路複用器selector上,監聽accept事件,代碼如下:
<code>selectionkey key = acceptorsvr.register( selector, selectionkey.op_accept, iohandler);</code>
步驟五:多路複用器線上程run方法的無限循環體内輪詢準備就緒的key,代碼如下:
<code>int</code> <code>num = selector.select();</code>
<code>set selectedkeys = selector.selectedkeys();</code>
<code>3</code>
<code>iterator it = selectedkeys.iterator();</code>
<code>4</code>
<code>while</code> <code>(it.hasnext()) {</code>
<code>5</code>
<code> </code><code>selectionkey key = (selectionkey)it.next();</code>
<code>6</code>
<code> </code><code>// ... deal with i/o event ...</code>
<code>7</code>
<code>}</code>
步驟六:多路複用器監聽到有新的用戶端接入,處理新的接入請求,完成tcp三次握手,建立實體鍊路,代碼示例如下:
<code>socketchannel channel = svrchannel.accept();</code>
步驟七:設定用戶端鍊路為非阻塞模式,示例代碼如下:
<code>channel.configureblocking(</code><code>false</code><code>);</code>
<code>channel.socket().setreuseaddress(</code><code>true</code><code>);</code>
步驟八:将新接入的用戶端連接配接注冊到reactor線程的多路複用器上,監聽讀操作,用來讀取用戶端發送的網絡消息,代碼如下:
<code>selectionkey key = socketchannel.register( selector, selectionkey.op_read, iohandler);</code>
步驟九:異步讀取用戶端請求消息到緩沖區,示例代碼如下:
<code>int</code> <code>readnumber = channel.read(receivedbuffer);</code>
步驟十:對bytebuffer進行編解碼,如果有半包消息指針reset,繼續讀取後續的封包,将解碼成功的消息封裝成task,投遞到業務線程池中,進行業務邏輯編排,示例代碼如下:
<code>01</code>
<code>object message = </code><code>null</code><code>;</code>
<code>02</code>
<code>while</code><code>(buffer.hasremain())</code>
<code>03</code>
<code>{</code>
<code>04</code>
<code> </code><code>bytebuffer.mark();</code>
<code>05</code>
<code> </code><code>object message = decode(bytebuffer);</code>
<code>06</code>
<code> </code><code>if</code> <code>(message == </code><code>null</code><code>)</code>
<code>07</code>
<code> </code><code>{</code>
<code>08</code>
<code> </code><code>bytebuffer.reset();</code>
<code>09</code>
<code> </code><code>break</code><code>;</code>
<code>10</code>
<code> </code><code>}</code>
<code>11</code>
<code> </code><code>messagelist.add(message );</code>
<code>12</code>
<code>13</code>
<code>if</code> <code>(!bytebuffer.hasremain())</code>
<code>14</code>
<code>bytebuffer.clear();</code>
<code>15</code>
<code>else</code>
<code>16</code>
<code> </code><code>bytebuffer.compact();</code>
<code>17</code>
<code>if</code> <code>(messagelist != </code><code>null</code> <code>& !messagelist.isempty())</code>
<code>18</code>
<code>19</code>
<code>for</code><code>(object messagee : messagelist)</code>
<code>20</code>
<code> </code><code>handlertask(messagee);</code>
<code>21</code>
步驟十一:将pojo對象encode成bytebuffer,調用socketchannel的異步write接口,将消息異步發送給用戶端,示例代碼如下:
<code>socketchannel.write(buffer);</code>
注意:如果發送區tcp緩沖區滿,會導緻寫半包,此時,需要注冊監聽寫操作位,循環寫,直到整包消息寫入tcp緩沖區,此處不贅述,後續netty源碼分析章節會詳細分析netty的處理政策。
當我們了解建立nio服務端的基本步驟之後,下面我們将前面的時間伺服器程式通過nio重寫一遍,讓大家能夠學習到完整版的nio服務端建立。
nio服務端通信序列圖