?
一、IoService接口
1、作用:IoService是創(chuàng)建服務(wù)的頂層接口,無(wú)論客戶端還是服務(wù)端,都是從它繼承實(shí)現(xiàn)的。
2、類結(jié)構(gòu)
常用接口為:IoService,IoAcceptor,IoConnector
常用類為:NioSocketAcceptor,NioSocketConnector
類圖如下:
?
?
先提出兩個(gè)問(wèn)題:
1. 為什么有了IoService接口還要定義AbstractIoService抽象類?
2. AbstractIoService抽象類與IoAcceptor(IoConnector)有什么區(qū)別?
分析:
? ? 1. IoService接口聲明了服務(wù)端的共有屬性和行為;
? ? 2. IoAcceptor接口繼承了IoService接口,并添加了服務(wù)端特有的接口屬性及方法,比如bind()方法,成為典型的服務(wù)端接口;
? ? 3. IoConnector接口同樣繼承了IoService接口,并添加了客戶端特有的接口屬性及方法,比如connect()方法,成為典型的客戶端接口;
---- IoService是IoAcceptor和IoConnector父接口,為什么不直接定義IoAcceptor和IoConnector接口呢,因?yàn)樗鼈冇泄餐奶攸c(diǎn),比如共同屬性,管理服務(wù)的方法等,所有IoService的出現(xiàn)是為了代碼復(fù)用。
? ? 4. AbstractIoService實(shí)現(xiàn)了IoService中管理服務(wù)的方法,比如getFilterChainBuilder方法---獲得過(guò)濾器鏈;
----為什么有了IoService接口還要定義AbstractIoService抽象類?一樣為了代碼的復(fù)用!AbstractIoService抽象類實(shí)現(xiàn)了服務(wù)端或客戶端的共有的管理服務(wù)的方法,不需要讓IoService接口的子類重復(fù)的實(shí)現(xiàn)這些方法;
? ? 5. AbstractIoAcceptor抽象類繼承了AbstractIoService抽象類并實(shí)現(xiàn)了IoAcceptor接口,成為了擁有管理服務(wù)端實(shí)現(xiàn)功能的服務(wù)端類;我們常用的NioSocketAcceptor就是它的子類;
? ? 6. AbstractIoConnector抽象類繼承了AbstractIoService抽象類并實(shí)現(xiàn)了IoConnector接口,成為了擁有管理客戶端實(shí)現(xiàn)功能的客戶端類;我們常用的NioSocketConnector就是它的子類;
----AbstractIoService抽象類與IoAcceptor(IoConnector)有什么區(qū)別?很清楚,AbstractIoService抽象類實(shí)現(xiàn)的是共有的管理服務(wù)的方法,只有管理功能的一個(gè)類;而兩個(gè)接口卻是不同的兩個(gè)服務(wù)角色----一個(gè)客戶端,一個(gè)服務(wù)端。
//創(chuàng)建一個(gè)非阻塞的server端的Socket acceptor = new NioSocketAcceptor(); //創(chuàng)建一個(gè)非阻塞的客戶端 IoConnector connector = new NioSocketConnector();
獲得IoSession的配置對(duì)象IoSessionConfig,通過(guò)它可以設(shè)置Socket連接的一些選項(xiàng)。
a. void setReadBufferSize(int size)
這個(gè)方法設(shè)置讀取緩沖的字節(jié)數(shù),但一般不需要調(diào)用這個(gè)方法,因?yàn)镮oProcessor?會(huì)自動(dòng)調(diào)整緩沖的大小。你可以調(diào)用setMinReadBufferSize()、setMaxReadBufferSize()方法,這樣無(wú)論IoProcessor?無(wú)論如何自動(dòng)調(diào)整,都會(huì)在你指定的區(qū)間。
b. void setIdleTime(IdleStatus status,int idleTime):
?
這個(gè)方法設(shè)置關(guān)聯(lián)在通道上的讀、寫或者是讀寫事件在指定時(shí)間內(nèi)未發(fā)生,該通道就進(jìn)入空閑狀態(tài)。一旦調(diào)用這個(gè)方法,則每隔idleTime都會(huì)回調(diào)過(guò)濾器、IoHandler中的sessionIdle()方法。
?
c. void setWriteTimeout(int time):
這個(gè)方法設(shè)置寫操作的超時(shí)時(shí)間。
d. void setUseReadOperation(boolean useReadOperation):
這個(gè)方法設(shè)置IoSession?的read()方法是否可用,默認(rèn)是false。
?
?
// 獲得IoSessionConfig對(duì)象 IoSessionConfig cfg=acceptor.getSessionConfig(); // 設(shè)置讀取數(shù)據(jù)的緩沖區(qū)大小() cfg.setReadBufferSize(2048); // 讀寫通道10秒內(nèi)無(wú)操作進(jìn)入空閑狀態(tài) cfg.setIdleTime(IdleStatus.BOTH_IDLE, 10); // 寫操作超時(shí)時(shí)間10秒 cfg.setWriteTimeout(10);?
Mina 最主要的工作就是把底層傳輸?shù)淖止?jié)碼轉(zhuǎn)換為 Java 對(duì)象,提供給應(yīng)用程序;或者把應(yīng)用程序返回的結(jié)果轉(zhuǎn)換為字節(jié)碼,交給底層傳輸 。這些都是由 IoFilter 完成的,因此 IoFilter 是 Mina 的精髓所在。
在 Mina 程序中, IoFilter 是必不可少的;有了它, Mina 的層次結(jié)構(gòu)才異常清晰:
IoFilter?? ----?? 消息過(guò)濾
IoHandler? ----?? 業(yè)務(wù)處理
??? Filter ,過(guò)濾器的意思。 IoFilter , I/O 操作的過(guò)濾器。 IoFilter 和 Servlet 中的過(guò)濾器一樣,主要用于攔截和過(guò)濾網(wǎng)絡(luò)傳輸中 I/O 操作的各種消息。在 Mina 的官方文檔中已經(jīng)提到了 IoFilter 的作用:
( 1 )記錄事件的日志( Mina 默認(rèn)提供了 LoggingFilter )
( 2 )測(cè)量系統(tǒng)性能
( 3 )信息驗(yàn)證
( 4 )過(guò)載控制
( 5 )信息的轉(zhuǎn)換 ( 主要就是編碼和解碼 )
( 6 )和其他更多的信息
??? IoService 實(shí)例會(huì)綁定一個(gè) DefaultIoFilterChainBuilder ---- 過(guò)濾器鏈,我們把自定義的各種過(guò)濾器( IoFilter )自由的插放在這個(gè)過(guò)濾器鏈上了,類似于一種可插拔的功能!
常用接口為: IoFilter , IoFilterChainBuilder
常用類為: IoFilterAdapter , DefaultIoFilterChainBuilder
ProtocolCodecFilter , LoggingFilter
類圖如下:
先提出兩個(gè)問(wèn)題:
1. 在IoService中如何添加多個(gè)IoFilter?
2. 如何自定義協(xié)議編解碼器?
分析:
a. IoFilter有2個(gè)實(shí)現(xiàn)類:IoFilterAdapter是個(gè)抽象的適配器類,我們可以根據(jù)需要擴(kuò)展這個(gè)類,并且有選擇的覆蓋過(guò)濾器的方法;所有方法的默認(rèn)把事件轉(zhuǎn)發(fā)到下一個(gè)過(guò)濾器;查看源碼如下:
?
?
public void sessionOpened(NextFilter nextFilter, IoSession session)throws Exception { nextFilter.sessionOpened(session); }?
?
b .ReferenceCountingFilter封裝了IoFilter實(shí)例,監(jiān)看調(diào)用該filter的對(duì)象的個(gè)數(shù),如果沒有任何對(duì)象調(diào)用該IoFilter,就自動(dòng)銷毀IoFilter;查看源碼如下:
?
?
public class ReferenceCountingFilter implements IoFilter { private final IoFilter filter; private int count = 0; public ReferenceCountingFilter(IoFilter filter) { this.filter = filter; } public void init() throws Exception { // no-op, will init on-demand in pre-add if count == 0 } public void destroy() throws Exception { } ……………略?
?
c. 實(shí)現(xiàn) IoFilterAdapter 的類有多個(gè),但是我們使用最多的就是
ProtocolCodecFilter---- 它是我們自定義編解碼器的入口?
添加過(guò)濾器----在IoService中如何添加多個(gè)IoFilter?如何代碼,我添加了2個(gè)過(guò)濾器:LoggingFilter和TextLineCodecFactory(源碼為入門的服務(wù)端程序)
// 創(chuàng)建一個(gè)非阻塞的server端的Socket acceptor = new NioSocketAcceptor(); // 設(shè)置日志過(guò)濾器 acceptor.getFilterChain().addLast("logger",new LoggingFilter()); // 設(shè)置過(guò)濾器(使用Mina提供的文本換行符編解碼器) acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); // 獲得IoSessionConfig對(duì)象 IoSessionConfig cfg = acceptor.getSessionConfig(); // 讀寫通道10秒內(nèi)無(wú)操作進(jìn)入空閑狀態(tài) cfg.setIdleTime(IdleStatus.BOTH_IDLE, 10); // 綁定邏輯處理器 acceptor.setHandler(new Demo1ServerHandler()); // 綁定端口 acceptor.bind(new InetSocketAddress(PORT)); logger.info("服務(wù)端啟動(dòng)成功... 端口號(hào)為:" + PORT);
?
如果要是使用LoggingFilter類,則需要需要導(dǎo)入slf4j-log4j12-1.7.5.jar、slf4j-api-1.7.5.jar、log4j-1.2.13.jar三個(gè)包,然后在src目錄下面添加log4j的配置文件
?
?
log4j.rootLogger=DEBUG,MINA,file log4j.appender.MINA=org.apache.log4j.ConsoleAppender log4j.appender.MINA.layout=org.apache.log4j.PatternLayout log4j.appender.MINA.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss,SSS} %-5p %c{1} %x - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/minademos.log log4j.appender.file.MaxFileSize=5120KB log4j.appender.file.MaxBackupIndex=10 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[VAMS][%d] %p | %m | [%t] %C.%M(%L)%n
?
?四、IoHandler接口
?
IoHandler是Mina實(shí)現(xiàn)其業(yè)務(wù)邏輯的頂級(jí)接口;它相當(dāng)簡(jiǎn)單,你就理解它是根據(jù)事件觸發(fā)的簡(jiǎn)單應(yīng)用程序即可。
在IoHandler中定義了7個(gè)方法,根據(jù)I/O事件來(lái)觸發(fā)對(duì)應(yīng)的方法:
?
import java.io.IOException; public interface IoHandler { void sessionCreated(IoSession session) throws Exception; void sessionOpened(IoSession session) throws Exception; void sessionClosed(IoSession session) throws Exception; void sessionIdle(IoSession session, IdleStatus status) throws Exception; void exceptionCaught(IoSession session, Throwable cause) throws Exception; void messageReceived(IoSession session, Object message) throws Exception; void messageSent(IoSession session, Object message) throws Exception; }
?
sessionCreated:當(dāng)一個(gè)新的連接建立時(shí),由I/O processor thread調(diào)用;
?
sessionOpened:當(dāng)連接打開是調(diào)用;
?
messageReceived:當(dāng)接收了一個(gè)消息時(shí)調(diào)用;
?
messageSent:當(dāng)一個(gè)消息被(IoSession#write)發(fā)送出去后調(diào)用;
?
sessionIdle:當(dāng)連接進(jìn)入空閑狀態(tài)時(shí)調(diào)用;
?
sessionClosed:當(dāng)連接關(guān)閉時(shí)調(diào)用;
?
exceptionCaught:當(dāng)實(shí)現(xiàn)IoHandler的類拋出異常時(shí)調(diào)用;
?
? ? 一般情況下,我們最關(guān)心的只有messageReceived方法,接收消息并處理,然后調(diào)用IoSession的write方法發(fā)送出消息!(注意:這里接收到的消息都是Java對(duì)象,在IoFilter中所有二進(jìn)制數(shù)據(jù)都被解碼啦!)
?
? ? 一般情況下很少有人實(shí)現(xiàn)IoHandler接口,而是繼承它的一個(gè)實(shí)現(xiàn)類IoHandlerAdapter,這樣不用覆蓋它的7個(gè)方法,只需要根據(jù)具體需求覆蓋其中的幾個(gè)方法就可以!
?
Iohandler的7個(gè)方法其實(shí)是根據(jù)session的4個(gè)狀態(tài)值間變化來(lái)調(diào)用的:
?
Connected:會(huì)話被創(chuàng)建并使用;
?
Idle:會(huì)話在一段時(shí)間(可配置)內(nèi)沒有任何請(qǐng)求到達(dá),進(jìn)入空閑狀態(tài);
?
Closing:會(huì)話將被關(guān)閉(剩余message將被強(qiáng)制flush);
?
Closed:會(huì)話被關(guān)閉;
?
?
狀態(tài)轉(zhuǎn)換圖如下
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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