官术网_书友最值得收藏!

2.3.3 NIO創建的TimeServer源碼分析

我們將在TimeServer例程中給出完整的NIO創建的時間服務器源碼。

代碼清單2-8 NIO時間服務器 TimeServer

我們對NIO創建的TimeServer進行簡單分析下,16~23行跟之前的一樣,設置監聽端口。24~25行創建了一個被稱為MultiplexerTimeServer的多路復用類,它是個一個獨立的線程,負責輪詢多路復用器Selctor,可以處理多個客戶端的并發接入。現在我們繼續看MultiplexerTimeServer的源碼。

代碼清單2-8 NIO時間服務器 MultiplexerTimeServer

由于這個類相比于傳統的Socket編程會稍微復雜一些,在此展開進行詳細分析,我們從如下幾個關鍵步驟來講解多路復用處理類。

(1)30~42行為構造方法,在構造方法中進行資源初始化,創建多路復用器Selector、ServerSocketChannel,對Channel和TCP參數進行配置。例如,將ServerSocketChannel設置為異步非阻塞模式,它的backlog設置為1024。系統資源初始化成功后,將ServerSocket Channel注冊到Selector,監聽SelectionKey.OP_ACCEPT操作位;如果資源初始化失敗(例如端口被占用),則退出。

(2)55~77行在線程的run方法的while循環體中循環遍歷selector,它的休眠時間為1s,無論是否有讀寫等事件發生,selector每隔1s都被喚醒一次,selector也提供了一個無參的select方法。當有處于就緒狀態的Channel時,selector將返回就緒狀態的Channel的SelectionKey集合,通過對就緒狀態的Channel集合進行迭代,可以進行網絡的異步讀寫操作。

(3)92~99行處理新接入的客戶端請求消息,根據SelectionKey的操作位進行判斷即可獲知網絡事件的類型,通過ServerSocketChannel的accept接收客戶端的連接請求并創建SocketChannel實例,完成上述操作后,相當于完成了TCP的三次握手,TCP物理鏈路正式建立。注意,我們需要將新創建的SocketChannel設置為異步非阻塞,同時也可以對其TCP參數進行設置,例如TCP接收和發送緩沖區的大小等,作為入門的例子,例程沒有進行額外的參數設置。

(4)100~125行用于讀取客戶端的請求消息,首先創建一個ByteBuffer,由于我們事先無法得知客戶端發送的碼流大小,作為例程,我們開辟一個1M的緩沖區。然后調用SocketChannel的read方法讀取請求碼流。注意,由于我們已經將SocketChannel設置為異步非阻塞模式,因此它的read是非阻塞的。使用返回值進行判斷,看讀取到的字節數,返回值有以下三種可能的結果。

◎ 返回值大于0:讀到了字節,對字節進行編解碼;

◎ 返回值等于0:沒有讀取到字節,屬于正常場景,忽略;

◎ 返回值為-1:鏈路已經關閉,需要關閉SocketChannel,釋放資源。

當讀取到碼流以后,我們進行解碼,首先對readBuffer進行flip操作,它的作用是將緩沖區當前的limit設置為position,position設置為0,用于后續對緩沖區的讀取操作。然后根據緩沖區可讀的字節個數創建字節數組,調用ByteBuffer的get操作將緩沖區可讀的字節數組復制到新創建的字節數組中,最后調用字符串的構造函數創建請求消息體并打印。如果請求指令是"QUERY TIME ORDER"則把服務器的當前時間編碼后返回給客戶端,下面我們看看異步發送應答消息給客戶端的情況。

(5)127~135行將應答消息異步發送給客戶端。我們看下關鍵代碼,首先將字符串編碼成字節數組,根據字節數組的容量創建ByteBuffer,調用ByteBuffer的put操作將字節數組復制到緩沖區中,然后對緩沖區進行flip操作,最后調用SocketChannel的write方法將緩沖區中的字節數組發送出去。需要指出的是,由于SocketChannel是異步非阻塞的,它并不保證一次能夠把需要發送的字節數組發送完,此時會出現“寫半包”問題,我們需要注冊寫操作,不斷輪詢Selector將沒有發送完的ByteBuffer發送完畢,可以通過ByteBuffer的hasRemain()方法判斷消息是否發送完成。此處僅僅是個簡單的入門級例程,沒有演示如何處理“寫半包”場景,后續的章節會有詳細說明。

使用NIO創建TimeServer服務器完成之后,我們繼續學習如何創建NIO客戶端。首先還是通過時序圖了解關鍵步驟和過程,然后結合代碼進行詳細分析。

主站蜘蛛池模板: 容城县| 同德县| 兴义市| 团风县| 贞丰县| 门源| 云霄县| 永泰县| 米易县| 措美县| 乌拉特中旗| 白银市| 灌南县| 盐城市| 敖汉旗| 虹口区| 六安市| 驻马店市| 陆良县| 台湾省| 平安县| 阳东县| 博湖县| 夏河县| 孟州市| 神农架林区| 华容县| 黄冈市| 昔阳县| 都兰县| 泗水县| 大石桥市| 崇礼县| 周口市| 高淳县| 岑巩县| 平陆县| 皮山县| 德保县| 景德镇市| 昔阳县|