當前位置:編程學習大全網 - 編程語言 - 瘋狂Java講義:使用NIO實現非阻塞Socket通信(1)

瘋狂Java講義:使用NIO實現非阻塞Socket通信(1)

  使用NIO實現非阻塞Socket通信

 從JDK 開始 Java提供的NIO API來開發高性能網絡服務器 前面介紹的網絡通信程序是基於阻塞式API的 即當程序執行輸入 輸出操作後 在這些操作返回之前會壹直阻塞該線程 所以服務器必須為每個客戶端都提供壹條獨立線程進行處理 當服務器需要同時處理大量客戶端時 這種做法會導致性能下降 使用NIO API則可以讓服務器使用壹個或有限幾個線程來同時處理連接到服務器上的所有客戶端

 如果讀者忘記了NIO裏Channel Buffer Charset等API的概念和用法 讀者可以再次閱讀本書第 章關於新IO的內容

 Java的NIO為非阻塞式的Socket通信提供了如下幾個特殊類

 Selector:它是SelectableChannel對象的多路復用器 所有希望采用非阻塞方式進行通信的Channel都應該註冊到Selector對象 可通過調用此類的靜態open()方法來創建Selector實例 該方法將使用系統默認的Selector來返回新的Selector

 Selector可以同時監控多個SelectableChannel的IO狀況 是非阻塞IO的核心 壹個Selector實例有 個SelectionKey的集合

 所有SelectionKey集合 代表了註冊在該Selector上的Channel 這個集合可以通過keys()方法返回

 被選擇的SelectionKey集合 代表了所有可通過select()方法監測到 需要進行IO處理的Channel 這個集合可以通過selectedKeys()返回

 被取消的SelectionKey集合 代表了所有被取消註冊關系的Channel 在下壹次執行select()方法時 這些Channel對應的SelectionKey會被徹底刪除 程序通常無須直接訪問該集合

 除此之外 Selector還提供了系列和select()相關的方法 如下所示

 int select() 監控所有註冊的Channel 當它們中間有需要處理的IO操作時 該方法返回 並將對應的SelectionKey加入被選擇的SelectionKey集合中 該方法返回這些Channel的數量

 int select(long timeout) 可以設置超時時長的select()操作

 int selectNow() 執行壹個立即返回的select()操作 相對於無參數的select()方法而言 該方法不會阻塞線程

 Selector wakeup() 使壹個還未返回的select()方法立刻返回

 SelectableChannel:它代表可以支持非阻塞IO操作的Channel對象 可以將其註冊到Selector上 這種註冊的關系由SelectionKey實例表示

 Selector對象提供了壹個select()方法 該方法允許應用程序同時監控多個IO Channel

 應用程序可調用SelectableChannel 的register()方法將其註冊到指定Selector上 當該Selector上某些SelectableChannel上有需要處理的IO操作時 程序可以調用Selector實例的select()方法獲取它們的數量 並可以通過selectedKeys()方法返回它們對應的SelectKey集合 通過該集合就可以獲取所有需要處理IO操作的SelectableChannel集

 SelectableChannel對象支持阻塞和非阻塞兩種模式(所有channel默認都是阻塞模式) 必須使用非阻塞式模式才可以利用非阻塞IO操作

 SelectableChannel提供了如下兩個方法來設置和返回該Channel的模式狀態

 SelectableChannel configureBlocking(boolean block) 設置是否采用阻塞模式

 boolean isBlocking() 返回該Channel是否是阻塞模式

 不同的SelectableChannel所支持的操作不壹樣 例如ServerSocketChannel代表壹個ServerSocket 它就只支持OP_ACCEPT操作

 SelectableChannel提供如下方法來返回它支持的所有操作

 int validOps() :返回壹個bit mask 表示這個channel上支持的IO操作

 在SelectionKey中 用靜態常量定義了 種IO操作 OP_READ( ) OP_WRITE( ) OP_CONNECT( ) OP_ACCEP( ) 這四值任意 個 個 個進行按位或的結果和相加的結果相等 而且它們任意 個 個 個相加的結果總是互不相同 所以系統可以根據validOps()方法的返回值確定該SelectableChannel支持的操作 例如返回 我們知道它支持讀( )和寫( )

 除此之外 SelectableChannel還提供了如下幾個方法來獲取它的註冊狀態

 boolean isRegistered() 返回該Channel是否已註冊在壹個或多個Selector上

 SelectionKey keyFor(Selector sel) 返回該Channel和sel Selector之間的註冊關系 如果不存在註冊關系 則返回null

 SelectionKey:該對象代表SelectableChannel和Selector之間的註冊關系

 ServerSocketChannel:支持非阻塞操作 對應於java net ServerSocket這個類 提供了TCP協議IO接口 只支持OP_ACCEPT操作 該類也提供了accept()方法 功能相當於ServerSocket提供的accept()方法

 SocketChannel:支持非阻塞操作 對應於java net Socket這個類 提供了TCP協議IO接口 支持OP_CONNECT OP_READ和OP_WRITE操作 這個類還實現了ByteChannel接口 ScatteringByteChannel接口和GatheringByteChannel接口 所以可以直接通過SocketChannel來讀寫ByteBuffer對象

 圖 顯示了使用NIO實現非阻塞式服務器的示意圖

圖 NIO的非阻塞式服務器示意

 從圖 中可以看出 服務器上所有Channel(包括ServerSocketChannel和SocketChannel)都需要向Selector註冊 而該Selector則負責監視這些Socket的IO狀態 當其中任意壹個或多個Channel具有可用的IO操作時 該Selector的select()方法將會返回大於 的整數 該整數值就表示該Selector上有多少個Channel具有可用的IO操作 並提供了selectedKeys()方法來返回這些Channel對應的SelectionKey集合 正是通過Selector 使得服務器端只需要不斷地調用Selector實例的select()方法即可知道當前所有Channel是否有需要處理的IO操作

 當Selector上註冊的所有Channel都沒有需要處理的IO操作時 select()方法將被阻塞 調用該方法的線程被阻塞

 本示例程序使用NIO實現了多人聊天室的功能 服務器使用循環不斷獲取Selector的select()方法返回值 當該返回值大於 時就處理該Selector上被選擇SelectionKey所對應的Channel

 服務器端需要使用ServerSocketChannel來監聽客戶端的連接請求 Java中該類的設計比較糟糕 它不是ServerSocket的完整抽象 所以不能直接讓該Channel監聽某個端口 而且不允許使用ServerSoceket的getChannel()方法來獲取ServerSocketChannel實例 程序必須先調用它的socket()方法獲得關聯ServerSocket對象 再用該ServerSocket對象綁定到來指定監聽IP和端口 創建壹個可用的ServerSocketChannel需采用如下代碼片段

 //通過open方法來打開壹個未綁定的ServerSocketChannel實例

 ServerSocketChannel server = ServerSocketChannel open()

 InetSocketAddress isa = new InetSocketAddress( )

 //將該ServerSocketChannel綁定到指定IP地址

 server socket() bind(isa)

 如果需要使用非阻塞方式來處理該ServerSocketChannel 還應該設置它的非阻塞模式 並將其註冊到指定的Selector 如下代碼片段

 //設置ServerSocket以非阻塞方式工作

 server configureBlocking(false)

 //將server註冊到指定Selector對象

 server register(selector SelectionKey OP_ACCEPT)

? 返回目錄 瘋狂Java講義

? 編輯推薦

? Java程序性能優化 讓妳的Java程序更快 更穩定

? 新手學Java 編程

lishixinzhi/Article/program/Java/hx/201311/27263

  • 上一篇:LOGO國際象棋
  • 下一篇:DOS的每個命令分別是什麽意思
  • copyright 2024編程學習大全網