從名字上看知道IoFilter應該是一個過濾器,不錯,它確實是一個過濾器,它和Servlet中的過濾器類似,主要用于攔截和過濾I/O操作中的各種信息。在Mina的官方文檔中已經提到了IoFilter的作用:
(1)記錄事件的日志(這個在本文中關于LoggingFilter的講述中會提到)
(2)測量系統性能
(3)信息驗證
(4)過載控制
(5)信息的轉換 (例如:編碼和解碼,這個會在關于ProtocolCodecFilter的講述中會提到)
(6)和其他更多的信息
?
還是上一篇文檔一樣,先提出幾個問題,然后沿著這幾個問題的思路一個一個的對IoFilter進行講解。
? (1)什么時候需要用到IoFilter,如果在自己的應用中不添加過濾器可以嗎?
? (2)如果在IoService中添加多個過濾器可以嗎?若可以,如何進行添加,這多個過濾??
?????? 器是如何工作的?
? (3)Mina中提供了協議編、解碼器,IoFilter也可以實現IO數據的編解碼功能,在實際
?????? 的使用中如何選擇?
?
在開始對上面的問題進行討論前,為了對IoFilter提供的方法有一個具體的了解,先對Mina自身提供的一個最簡單的過濾器進行一些講解----LoggingFilter(源碼在附件中,配有中文翻譯)。
?
首先還是看一下LoggingFilter中提供的幾個方法。列舉如下(方法中的參數就不再給出,完整方法的實現請參考附件中LoggingFilter的源碼):
(1)sessionCreated()
(2)sessionOpened()
(3)sessionClosed()
(4)sessionIdle()
(5)exceptionCaught()
(6)messageReceived()
(7)messageSent()
(8)filterWrite()
(9)filterClose()
這幾個方法都由相應會話(或者說是連接的狀態,讀、寫、空閑、連接的開閉等)的狀態的改變來觸發的。當一個會話開啟時,LoggingFilter捕獲到會話開啟的事件,會觸發sessionCreated()方法,記錄該會話開啟的日志信息。同樣當一個會話發送數據時,Logging捕獲到會話發送消息的事件會記錄消息發送的日志信息。這里只是給出messageReceived()的完成方法的實現,其他方法的完整實現請參考附件中LoggingFilter的源碼。
?
?/**
? * 記錄會話接收信息時的信息,然后將該信息傳遞到過濾器鏈中的下一個過濾器
? * */
?public void messageReceived(NextFilter nextFilter, IoSession session,
???Object message) {
??if (SessionLog.isInfoEnabled(session)) {
???SessionLog.info(session, "RECEIVED: " + message);
??}
??nextFilter.messageReceived(session, message);
?}?
LoggingFilter繼承與IoFilterAdpater,IoFilterAdpater是IoFilter的一個實現類,該類只是提供了IoFilter方法的簡單實現----將傳遞到各方法中的消息轉發到下一個過濾器中。你可以根據自己的需求繼承IoFilterAdpater,并重寫相關的方法。LoggingFilter就是重寫了上面提到的幾個方法,用于記錄當前的會話各種操作的日志信息。通過上面的例子,我們可以大體的了解了IoFilter的基本功能:根據當前會話狀態,來捕獲和處理當前會話中所傳遞的消息。
?
IoFilter的UML圖如下:
?
從上面的類圖我們可以清晰的看到IoFilter是一個接口,它有兩個具體的實現類:
IoFilterAdpater:該類提供了IoFilter所有方法的方法體,但是沒有任何邏輯處理,你可以根據你具體的需求繼承該類,并重寫相關的方法。IoFilterAdpater是在過濾器中使用的較多的一個類。
ReferenceCountingIoFilter:該類封裝IoFilter的實例,它使用監視使用該IoFilter的對象的數量,當沒有任何對象使用該IoFilter時,該類會銷毀該IoFilter。
IoFilterAdpater有三個子類,它們的作用分別如下:
LoggingFilter:日志工具,該類處理記錄IoFilter每個狀態觸發時的日志信息外不對數據做任何處理。它實現了IoFilter接口的所有方法。你可以通過閱讀該類的源碼學習如何實現你自己的IoFilter。
ExcuterFilter:這個Mina自身提供的一個線程池,在Mina中你可以使用這個類配置你自己的線程池,由于創建和銷毀一個線程,需要耗費很多資源,特別是在高性能的程序中這點尤其重要,因此在你的程序中配置一個線程池是很重要的。它有助于你提高你的應用程序的性能。關于配置Mina的線程池在后續的文檔中會給出詳細的配置方法。
ProtocolFilter:該類是Mina提供的一個協議編解碼器,在socket通信中最重要的就是協議的編碼和解碼工作,Mina提供了幾個默認的編解碼器的實現,在下面的例子中使用了ObjectSerializationCodecFactory,這是Mina提供的一個Java對象的序列化和反序列化方法。使用這個編解碼器,你可以在你的Java客戶端和服務器之間傳遞任何類型的Java對象。但是對于不同的平臺之間的數據傳遞需要自己定義編解碼器,關于這點的介紹會在后續的文檔中給出。
?
為了更加清楚的理解這個過濾器的作用我們先來看一個簡單的例子,這個例子的功能就是服務器在客戶端連接到服務器時創建一個會話,然后向客戶端發送一個字符串(完整的源碼在附件中,這里只給出程序的簡要內容):
ServerMain:
public class ServerMain {
public static void main(String[] args) throws IOException {
SocketAddress address = new InetSocketAddress("localhost", 4321);
IoAcceptor acceptor = new SocketAcceptor();
IoServiceConfig config = acceptor.getDefaultConfig();
// 配置數據的編解碼器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
// 綁定服務器端口
acceptor.bind(address, new ServerHandler());
System.out.println(" 服務器開始在 8000 端口監聽 .......");
}
}
ServerHandler:
public class ServerHandler extends IoHandlerAdapter {
// 創建會話
public void sessionOpened(IoSession session) throws Exception {
System.out.println(" 服務器創建了會話 ");
session.write(" 服務器創建會話時發送的信息 。");
}
// 發送信息
public void messageSent(IoSession session, Object message) throws Exception {
}
// 接收信息
public void messageReceived(IoSession session, Object message)
throws Exception {
}
}
ClientMain:
public class ClientMain {
public static void main(String[] args) {
SocketAddress address = new InetSocketAddress("localhost", 4321);
IoConnector connector = new SocketConnector();
IoServiceConfig config = connector.getDefaultConfig();
// 配置數據的編解碼器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
config.getFilterChain().addLast("logger", new LoggingFilter());
// 連接到服務器
connector.connect(address, new ClientHandler());
System.out.println(" 已經連接到了服務器 " + address);
}
}
ClientHandler:
public class ClientHandler extends IoHandlerAdapter {
// 發送信息
public void messageSent(IoSession session, Object message) throws Exception {
}
// 接收信息
public void messageReceived(IoSession session, Object message)
throws Exception {
System.out.println(" 客戶端接收到的服務器的信息是 " + message);
}
}
?
其中ServerMain和ClientMain分別是服務器和客戶端的主程序,ServerHandler和ClientHandler是服務器和客戶端的數據處理句柄,關于IoHandler會在后面的文檔中做詳細的講解,這里只是簡單說明一下,IoHandler主要是對數據進行邏輯操作,也可以理解為程序的業務邏輯層。其中:
// 配置數據的編解碼器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
?
這行代碼的功能是將網絡傳輸中的數據在發送時編碼成二進制數據,解碼時將二進制數據還原成一個對象或者是基本類型的數據。
運行這個程序會得到如下結果:
已經連接到了服務器 localhost/127.0.0.1:4321
2009-7-9 23:36:46 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] CREATED
2009-7-9 23:36:46 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] OPENED
2009-7-9 23:36:46 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] RECEIVED: 服務器創建會話時發送的信息 。
客戶端接收到的服務器的信息是 服務器創建會話時發送的信息 。
?
其中的紅字部分是LoggingFilter打印出的事件信息。黑體部分是程序中System.out的輸出。在ClientMain中的這兩行代碼是向過濾器鏈中添加IoFilter:
// 配置數據的編解碼器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
config.getFilterChain().addLast("logger", new LoggingFilter());//添加日志工具
?
上圖表示了數據在本程序中通過過濾器鏈的過程,日志過濾器是根據會話(IoSession)的狀態(創建、開啟、發送、接收、異常等等)來記錄會話的事件信息的,編解碼器是根據會話的接收和發送數據來觸發事件的,從這里我們也可以了解通過過濾器我們可以專門針對會話的某個或某幾個狀態來專門處理相關的事件,如異常事件,我們可以專門定義一個Exception的IoFilter來處理Mina在通信中所發生的異常信息。
?
還有一個比較有意思的問題是,假如我們將上面過濾器的順序該成下面的樣子:
config.getFilterChain().addLast("logger", new LoggingFilter());//添加日志工具
// 配置數據的編解碼器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
程序的執行結果如下:
已經連接到了服務器 localhost/127.0.0.1:4321
2009-7-10 0:30:12 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] CREATED
2009-7-10 0:30:12 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] OPENED
2009-7-10 0:30:12 org.apache.mina.util.SessionLog info
信息: [localhost/127.0.0.1:4321] RECEIVED: DirectBuffer[pos=0 lim=56 cap=1024: 00 00 00 34 AC ED 00 05 74 00 2D 20 20 E6 9C 8D E5 8A A1 E5 99 A8 E5 88 9B E5 BB BA E4 BC 9A E8 AF 9D
E6 97 B6 E5 8F 91 E9 80 81 E7 9A 84 E4 BF A1 E6 81 AF 20 E3 80 82]
客戶端接收到的服務器的信息是 服務器創建會話時發送的信息 。
?
很明顯的是在順序變化了之后,日志中多了接收到的二進制數據,這是因為在上面數據已經有解碼器將數據還原成了Java對象,所以我們就看不到二進制數據了,而在順序變換后,由于先執行的是打印信息,此時的數據還沒有還原成java對象,所以接收到的數據是二進制的。
?
在上面的例子中我們清楚了整個IoFilter或者是IoFilter的工作流程,那么IoFilter在Mina中的作用如何?所有的數據在發送到Mina程序中時,數據都是先通過IoFilter,經過處理后再轉發到業務層。這里IoFilter就起到了一個承上啟下的作用。
?
到這里我們就可以回答本文開始提到的問題了:
?(1)什么時候需要用到IoFilter,如果在自己的應用中不添加過濾器可以嗎?
在你自己的程序中可以添加過濾器,也可以不添加,但是在數據發送之前,所發送的數據必須轉換成二進制數據,這個可以有IoFilter完成,也可以由ProtocolCodecFilter完成(關于這個問題會在后面的文章中詳細講述),否則Mina會拋出Write requests must be transformed to class org.apache.mina.common.ByteBuffer: 異常。這是因為網絡中傳輸的數據只能是二進制數據。因此無論添加不添加過濾器,都必須將要發送的數據轉換成二進制數據。
(2)如果在IoService中添加多個過濾器可以嗎?若可以,如何進行添加,這多個過濾器是如何工作的?
???? 在IoService中可以添加多個過濾器,這個在上面的程序中已經給處理,添加的方式也很簡單,通過程序一目了然。
(3)Mina中提供了協議編、解碼器,IoFilter也可以實現IO數據的編解碼功能,在實際的使用中如何選擇?
???? Mina的編解碼器在Mina的使用中是最關鍵的一個問題,特別是在不同語言之間進行通信的時候,比如Java和C/C++等,由于Mina自身沒有提供這些編解碼器,所以需要自己來實現。Mina提供了一個Decoder/Encoder,你可以實現兩個類來完成不同平 之間的通信。關于這個問題會在后面的文檔給出具體的實習方法。
?
至此,關于IoFilter的作用就講述完了,希望對你能有所幫助。:)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
