如何封裝一個非阻塞式 Java 網(wǎng)絡(luò)庫
1. 前言
到目前為止,我們已經(jīng)學(xué)完了 Java 網(wǎng)絡(luò)編程的所有關(guān)鍵知識點,如果能將這些知識點靈活應(yīng)用到項目中,那再好不過了。本小節(jié)將從實戰(zhàn)的角度出發(fā),展示如何基于 Java NIO 封裝一個非阻塞式網(wǎng)絡(luò)庫。核心是采用事件反應(yīng)器模型,將復(fù)雜的 SocketChannel、ServerSocketChannel、ByteBuffer、Selector 進行抽象,將繁瑣的數(shù)據(jù)讀寫操作加以封裝,以便應(yīng)用程序調(diào)用。
本項目提供的完整代碼路徑:
2. 系統(tǒng)類圖
在 OOD 的思想體系下,現(xiàn)實世界中無論是具體的、還是抽象的事物,都是對象,現(xiàn)實世界就是由對象組成的。對象有自己的屬性和行為,對象之間可以產(chǎn)生聯(lián)系。把具有相同屬性和行為的對象叫做同一類對象,抽象出類。OOD 的思想非常符合人的思維方式,更容易建模和抽象。如果你對 OOD 不是很熟悉的話,可以查閱相關(guān)資料學(xué)習。
我們現(xiàn)在的工作是要把 Java NIO 中的各個組件加以抽象,抽象出一個或若干個類,通過 UML 展現(xiàn)出來,類圖如下:

下來我們就解釋一下以上幾個接口和實現(xiàn)類的功能:
| 類名 | 功能 |
|---|---|
| Poller | Java NIO Selector 事件多路復(fù)用機制的封裝,實現(xiàn)事件循環(huán)機制,是一個事件反應(yīng)器,是一個功能實現(xiàn)類 |
| IOHandler | 是 Poller 的配套類,響應(yīng)accept、connect、read、write事件,是一個 Java 接口 |
| SocketHandler | 是一個功能類,實現(xiàn)了 IOHandler 接口,將一些通用的邏輯加以封裝 |
| Acceptor | 實現(xiàn) TCP 服務(wù)器監(jiān)聽功能,繼承自 SocketHandler |
| TcpHandler | TCP 客戶端、服務(wù)器邏輯的封裝,繼承自 SocketHandler。主要完成客戶端連接,服務(wù)器接收新連接,數(shù)據(jù)收發(fā),關(guān)閉連接的功能 |
| IOAdapter | 事件循環(huán)機制向應(yīng)用層提供的一個回調(diào)接口,一般由 IOHandler 調(diào)用 |
| AbstractAdapter | 是一個 Java 接口,主要完成通用邏輯,實現(xiàn)了 IOAdapter 接口 |
| Listener | 事件監(jiān)聽接口,主要實現(xiàn)線程切換的功能 |
| CustomEventObject | 代表一個具體事件,是 Listener 的配套類 |
| IOThread | 對 Java 線程的封裝,聚合了 Poller 功能。我們說過 Java 的 Selector 其實是同步的,需要一個線程調(diào)用 select 監(jiān)聽事件 |
| ThreadPool | 對 IOThread 的封裝,提供線程池功能 |
3. 接口設(shè)計
軟件的接口是指軟件模塊對外提供的一組函數(shù)或者方法,目的是讓別的模塊訪問本模塊的功能,以達到組件復(fù)用的目的。根據(jù)模塊邏輯復(fù)雜度的不同,接口由分為:系統(tǒng)接口、子系統(tǒng)接口、模塊接口、子模塊接口、類接口。關(guān)于模塊、子模塊、類的應(yīng)用都非常靈活,我們這里認為類就是最小的模塊。
本小節(jié)所說的接口是指 Java 接口。我們抽象了三個 Java 接口:Listener、IOHandler、IOAdapter,還有一個功能類 Poller?,F(xiàn)在對每個接口中的方法加以說明:
- Poller
| 類名 | 接口名 | 描述 |
|---|---|---|
| Poller | register | 將 IOHandler 實例添加到 Poller |
| start | 啟動一個 Poller 實例 | |
| close | 停止一個 Poller 實例 | |
| poll | Poller 進入事件循環(huán) |
- IOHandler
| 類名 | 接口名 | 描述 |
|---|---|---|
| IOHandler | handle_read | 是一個回調(diào)方法,當 Poller 監(jiān)聽到某個 IOHandler 注冊的讀事件觸發(fā)時,調(diào)用 handle_read |
| handle_write | 是一個回調(diào)方法,當 Poller 監(jiān)聽到某個 IOHandler 注冊的寫事件觸發(fā)時,調(diào)用 handle_write | |
| handle_accept | 是一個回調(diào)方法,當 Poller 監(jiān)聽到某個 IOHandler 注冊的accept事件觸發(fā)時,調(diào)用 handle_accept | |
| handle_connected | 是一個回調(diào)方法,當 Poller 監(jiān)聽到某個 IOHandler 注冊的connected事件觸發(fā)時,調(diào)用 handle_connected | |
| getSocketChannel | 用于獲取 IOHandler 對應(yīng)的 SocketChannel 對象 |
IOHandler 是一個抽象接口,TcpHandler 需要實現(xiàn)此接口,當然你也可以實現(xiàn)其他協(xié)議,只需要擴展 IOHandler 的接口即可。
- IOAdapter
| 類名 | 接口名 | 描述 |
|---|---|---|
| IOAdapter | onAccept | 當 IOHandler 收到一個新的 TCP 連接時,在 handle_read 中回調(diào)此方法 |
| onConnected | 當 io_hanIOHandlerdler 完成異步連接時,在 handle_connected 中回調(diào)此方法 | |
| onRead | IOHandler 會在 handle_read 中回調(diào)此方法 | |
| onWrite | IOHandler 會在 handle_write 中回調(diào)此方法 | |
| onClose | IOHandler 收到連接被關(guān)閉時,回調(diào)此方法 | |
| setSocketHandler | 向 IOAdapter 設(shè)置一個 IOHandler 對象 |
應(yīng)用層需要實現(xiàn) IOAdapter 的接口,并且要覆蓋接口中的方法,完成數(shù)據(jù)收發(fā)。
- Listener
| 類名 | 接口名 | 描述 |
|---|---|---|
| Listener | process | 處理異步事件 |
4. 總結(jié)
本小節(jié)主要是將前面小節(jié)介紹的 Java NIO 相關(guān)模塊進行一個抽象,形成一個非阻塞的 Java 網(wǎng)絡(luò)庫。采用的設(shè)計思想就是依賴倒轉(zhuǎn),將復(fù)雜的網(wǎng)絡(luò)編程細節(jié)進行封裝,讓應(yīng)用程序員不需要關(guān)注這些復(fù)雜的機制。
我們抽象出了一組接口和一組功能類,并對類的功能和接口中的方法進行了一一說明。本小節(jié)可以說是對整個系列內(nèi)容的總結(jié)和實踐。
陳子興 ·
2025 imooc.com All Rights Reserved |