詳解Java Socket通信封裝MIna框架
核心類
IoService :Mina中將服務(wù)端和客戶端都看成是服務(wù),這里提供統(tǒng)一接口IoService,這個(gè)接口的作用就是用來處理套接字機(jī)制。也正是IoService來監(jiān)聽消息返回消息這些步驟,可以說IoService就是我們Mina中核心
IoProcessor:這個(gè)接口在另一個(gè)線程上,負(fù)責(zé)檢查是否有數(shù)據(jù)在通道上讀寫,也就是說它也擁有自己的Selector,這是與我們使用JAVA NIO 編碼時(shí)的一個(gè)不同之處,通常在JAVA NIO 編碼中,我們都是使用一個(gè)Selector,也就是不區(qū)分IoService與IoProcessor 兩個(gè)功能接口。另外,IoProcessor 負(fù)責(zé)調(diào)用注冊(cè)在IoService 上的過濾器,并在過濾器鏈之后調(diào)用IoHandler
IoFilter : 定義了一些攔截器 , 和我們web中攔截器一樣,用來橫向攔截處理一些全局的操作(日志處理,編碼處理)。其中我們必須注意的是加解密消息。作為一個(gè)好的框架肯定是有默認(rèn)的攔截器的(TextLineCodecFactory )。默認(rèn)攔截器可以叫消息強(qiáng)制轉(zhuǎn)換為String類型。畢竟String最通用
IoHandler : 這個(gè)是我們處理消息的邏輯,前面的攔截器只是在接受是進(jìn)行一些驗(yàn)證、翻譯的功能。拿到數(shù)據(jù)之后我們需要做的事情就是在IoHandler中
各個(gè)擊破
IoService
首先我們已服務(wù)端NioSocketAcceptor為列,看看我們的服務(wù)類之間的結(jié)構(gòu)依賴關(guān)系
IoService是服務(wù)的鼻祖,無論在我們看來的服務(wù)端還是客戶端都得繼承它(間接繼承)。在IoService中我們會(huì)定義我們消息的處理過濾器(上文的攔截器),消息處理的業(yè)務(wù)類。在上文簡(jiǎn)介中我們知道,這一步其實(shí)是IoProcessor來完成,那么IoProcessor在什么出現(xiàn)呢。比如Mina框架中用來創(chuàng)建服務(wù)端類NioSocketAcceptor。他直接繼承了AbstractPollingIoAcceptor。而AbstractPollingIoAcceptor類中根據(jù)參數(shù)創(chuàng)建了我們需要的IoProcessor.從而我們有了IoProcessor就可以執(zhí)行消息間的通信了。
所以過濾器、處理器實(shí)在我們服務(wù)啟動(dòng)之前配置好的。一旦啟動(dòng)成功就無法再修改了。我們服務(wù)端NioSocketAcceptor通過bind方法就可以綁定到指定端口上。我們這里的綁定實(shí)現(xiàn)了多態(tài)綁定。我們可以綁定多個(gè)服務(wù)。
/** * {@inheritDoc} */ @Override public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException { if (isDisposing()) { throw new IllegalStateException("The Accpetor disposed is being disposed."); } if (localAddresses == null) { throw new IllegalArgumentException("localAddresses"); } List<SocketAddress> localAddressesCopy = new ArrayList<>(); for (SocketAddress a : localAddresses) { checkAddressType(a); localAddressesCopy.add(a); } if (localAddressesCopy.isEmpty()) { throw new IllegalArgumentException("localAddresses is empty."); } boolean activate = false; synchronized (bindLock) { synchronized (boundAddresses) { if (boundAddresses.isEmpty()) { activate = true; } } if (getHandler() == null) { throw new IllegalStateException("handler is not set."); } try { Set<SocketAddress> addresses = bindInternal(localAddressesCopy); synchronized (boundAddresses) { boundAddresses.addAll(addresses); } } catch (IOException | RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e); } } if (activate) { getListeners().fireServiceActivated(); } }
在上面我們可以看到bind最后是去激活對(duì)應(yīng)的監(jiān)聽器。我們一個(gè)IoServer處理一個(gè)線程中的消息。我們監(jiān)聽器就是監(jiān)聽線程內(nèi)的消息。每一次的綁定都會(huì)有不同的監(jiān)聽器、ioSession去專門處理消息之間的通信。我們可以通過IoSession設(shè)置一些請(qǐng)求數(shù)據(jù)完成數(shù)據(jù)的權(quán)限驗(yàn)證。
在服務(wù)創(chuàng)建的時(shí)候我們正常需要設(shè)置IoSession的一些配置。通過getSessionConfig方法獲取IoSessionConfig。里面設(shè)置參數(shù)常用如下:
- setReadBufferSize : 設(shè)置讀取數(shù)據(jù)的緩沖區(qū)大小
- setMinReadBufferSize: 設(shè)置緩沖區(qū)最大值
- setMaxReadBufferSize: 設(shè)置緩沖區(qū)最小值
- setThroughputCalculationInterval: 設(shè)置通道計(jì)算時(shí)間 默認(rèn)3s
- setIdleTime(IdleStatus status, int idleTime): status 設(shè)置是一方還是雙方 , idLetime 是超過多久就會(huì)進(jìn)入空閑狀態(tài)
IoAcceptor acceptor=new NioSocketAcceptor(); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig.setIdleTime(IdleStatus.BOTH_IDLE,10);
IoFilter
在IoService中有獲取filter鏈的一個(gè)方法 DefaultIoFilterChainBuilder getFilterChain() , 我們需要做的就是定義過濾器,然后通過該方法獲取過濾鏈加入到請(qǐng)求鏈上。我們自定義過濾器也很簡(jiǎn)單,只需要繼承IoFilterAdapter這個(gè)類就好了。
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory( Charset.forName("UTF-8"),LineDelimeter.WINDOWS.getValue(),LineDelimiter. WINDOWS.getValue())) );
TextLineCodecFactory 這個(gè)類是Mina提供的編解碼工廠,這個(gè)工廠的特性是以換行符'\r\n'為結(jié)束通信的標(biāo)志。也就是說如果我們傳遞消息沒有換行符,另外一段會(huì)繼續(xù)
接受消息知道接受到'\r\n'才會(huì)接受,并把接受到的消息通過編解碼器轉(zhuǎn)到IoHandler層供業(yè)務(wù)層處理。(這里博主被坑在這里了)
IoHandler
到了這一步,我們的通信基本就已經(jīng)完成了。剩下的事情已經(jīng)和Mina基本沒多大關(guān)聯(lián)了。我們將在這里處理業(yè)務(wù)邏輯,使用到的就是Handler提供的接收消息和發(fā)送消息兩個(gè)功能。這里我們需要注意的是Handler提供messageReceived和messageSent并不是字面意思。前者就是消息的接受,但是后者并不是消息的發(fā)送。我們常用的發(fā)送消息是session.write方法。
總結(jié)
今天我們了解了Mina工作的流程,主要就是IoFilter和IoHandler實(shí)現(xiàn)消息的通信 。 千里之行始于足下,一點(diǎn)一點(diǎn)的進(jìn)步。下面貼出一份總結(jié)的圖譜幫助我們理解Mina流程
以上就是詳解Java Socket通信封裝MIna框架的詳細(xì)內(nèi)容,更多關(guān)于Java Socket通信封裝MIna框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Eclipse中改變默認(rèn)的workspace的方法及說明詳解
eclipse中改變默然的workspace的方法有哪幾種呢?接下來腳本之家小編給大家介紹Eclipse中改變默認(rèn)的workspace的方法及說明,對(duì)eclipse改變workspace相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-04-04通過實(shí)例了解Java 8創(chuàng)建Stream流的5種方法
這篇文章主要介紹了通過實(shí)例了解Java 8創(chuàng)建Stream流的5種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12java獲取nvidia顯卡信息的實(shí)現(xiàn)示例
現(xiàn)在的需求是要獲取nvidia顯卡的使用情況,本文主要介紹了java獲取nvidia顯卡信息的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02java面試題解LeetCode27二叉樹的鏡像實(shí)例
這篇文章主要為大家介紹了java面試題解LeetCode27二叉樹的鏡像實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01SpringBoot攔截器excludePathPatterns方法不生效的解決方案
這篇文章主要介紹了SpringBoot攔截器excludePathPatterns方法不生效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07解決SpringMVC使用@RequestBody注解報(bào)400錯(cuò)誤的問題
這篇文章主要介紹了解決SpringMVC使用@RequestBody注解報(bào)400錯(cuò)誤的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09