http://unbounder.iteye.com/blog/481396
http://unbounder.iteye.com/blog/481668
?
?
基于io包的阻塞式socket通信代碼簡單,在連接數(shù)很少的情況下是一個不錯的選擇。不過實際應(yīng)用中一個socket服務(wù)器采用傳統(tǒng)的阻塞式socket方式通信可能會是一場災(zāi)難,一路socket同時進行讀寫操作可能就需要兩條線程,如果需要并發(fā)一百路socket(這個量其實很小了),可能就是兩百條線程,大概幾分鐘后cpu占用率就是高居不下了。?
基于原生nio的socket通信時一種很好的解決方案,基于事件的通知模式使得多并發(fā)時不用維持高數(shù)量的線程,高并發(fā)的socket服務(wù)器的java實現(xiàn)成為現(xiàn)實。不過原生nio代碼十分復(fù)雜,無論編寫還是修改都是一件頭疼的事?!捌帘蔚讓拥姆爆嵐ぷ?,讓程序員將注意力集中于業(yè)務(wù)邏輯本身”,有需求就有生產(chǎn)力進步,于是各式各樣的nio框架涌現(xiàn)而出,而筆者使用到的是其中最常見的兩種:xSocket和MINA。?
1 xsocket框架
?
官網(wǎng):http://xsocket.sourceforge.net/?
xSocket是一套非常簡潔的nio框架,利用這套框架,你可以在完全不了解nio的情況下設(shè)計出高并發(fā)的socket服務(wù)器。?
server端代碼:?
- public ? class ?ProjectServer? extends ?Thread?{??
- ??????
- ???? private ? static ? final ? int ?PORT= 9099 ;??
- ??????
- ???? public ? void ?run()?{??
- ????????IServer?srv?=? null ;??
- ???????? try ?{??
- ???????????? //建立handler ??
- ????????????srv?=? new ?Server(PORT,? new ?ProjectHandle());??
- ????????}? catch ?(UnknownHostException?e)?{??
- ???????????? //?TODO?Auto-generated?catch?block ??
- ????????????e.printStackTrace();??
- ????????}? catch ?(IOException?e)?{??
- ???????????? //?TODO?Auto-generated?catch?block ??
- ????????????e.printStackTrace();??
- ????????}??
- ???????? //服務(wù)器運行 ??
- ????????srv.run();??
- ????????System.out.println( "The?ProjectServer?start?on?port:?" +PORT);??
- ????}??
- ??????
- ???? public ? static ? void ?main(String[]?args)?{??
- ????????ProjectServer?projectServer?=? new ?ProjectServer();??
- ????????projectServer.start();??
- ????}??
- }??
handler代碼?
- public ? class ?ProjectHandle? implements ?IDataHandler,?IConnectHandler,??
- ????????IDisconnectHandler?{??
- ???? /*?處理連接建立事件?*/ ??
- ???? @Override ??
- ???? public ? boolean ?onConnect(INonBlockingConnection?nbc)? throws ?IOException,??
- ????????????BufferUnderflowException,?MaxReadSizeExceededException?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????System.out.println(nbc.getId()?+? "is?connect!" );??
- ???????? return ? true ;??
- ????}??
- ??
- ???? /*?處理連接斷開事件?*/ ??
- ???? @Override ??
- ???? public ? boolean ?onDisconnect(INonBlockingConnection?nbc)? throws ?IOException?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????System.out.println(nbc.getId()?+? "is?disconnect!" );??
- ???????? return ? true ;??
- ????}??
- ??
- ???? /*?處理接受數(shù)據(jù)事件?*/ ??
- ???? @Override ??
- ???? public ? boolean ?onData(INonBlockingConnection?nbc)? throws ?IOException,??
- ????????????BufferUnderflowException,?ClosedChannelException,??
- ????????????MaxReadSizeExceededException?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????String?str?=?nbc.readStringByDelimiter( "\0" );??
- ????????System.out.println(str);??
- ???????? return ? true ;??
- ????}??
- }??
這里我們以"\0"為間隔讀取數(shù)據(jù),xSocket還提供按照長度讀取以及全部讀取到一個ByteBuffer幾種讀取數(shù)據(jù)的方式,如果傳遞過程中采取byte數(shù)組格式,在接受數(shù)據(jù)時還可以使用xSocket自帶的工具類DataConverter進行轉(zhuǎn)化。譬如接收數(shù)據(jù)時如此操作:?
- ByteBuffer?copyBuffer?=?ByteBuffer.allocate( 20000 );??
- nbc.read(copyBuffer);??
- copyBuffer.flip();??
- String?str?=?DataConverter.toString(copyBuffer,? "utf-8" );??
就可以將二進制格式的傳輸內(nèi)容還原為原始字符串了。?
需要說明的是雖然xSocket代碼簡單、開發(fā)快捷,但是由于太“上層”了,所以很多地方不利于coder自己控制。譬如socket通信中一個很常見的“半包連包”問題用xsocket處理起來就會很麻煩,而且如果傳輸協(xié)議是經(jīng)過特殊設(shè)計的,xsocket也無法像mina那樣自己編寫解碼器。當然如果你不是特別計較這些方面,那么非常適于上手的xSocket就是適合你的nio框架。?
?
?
3 MINA
?
項目主頁:http://mina.apache.org/?
閑話不說,上代碼?
- public ? class ?Server? extends ?Thread?{??
- ???? private ? static ? final ? int ?PORT?=? 23 ;??
- ??
- ???? public ? void ?run()?{??
- ????????IoAcceptor?acceptor?=? new ?NioSocketAcceptor();??
- ????????acceptor.setHandler( new ?TestHandler());??
- ????????acceptor.getFilterChain().addLast( "codec" ,??
- ???????????????? new ?ProtocolCodecFilter( new ?TextLineCodecFactory()));??
- ????????acceptor.getSessionConfig().setReadBufferSize( 2048 );??
- ????????acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,? 10 );??
- ???????? try ?{??
- ????????????acceptor.bind( new ?InetSocketAddress(PORT));??
- ????????}? catch ?(IOException?e)?{??
- ???????????? //?TODO?Auto-generated?catch?block ??
- ????????????e.printStackTrace();??
- ????????}??
- ????????System.out.println( "The?ProjectServer?start?on?port:?" ?+?PORT);??
- ????}??
- ??
- ???? public ? static ? void ?main(String[]?args)?{??
- ????????Server?server?=? new ?Server();??
- ????????server.start();??
- ????}??
- }??
- public ? class ?TestHandler? extends ?IoHandlerAdapter?{??
- ??
- ???? @Override ??
- ???? public ? void ?exceptionCaught(IoSession?session,?Throwable?cause)??
- ???????????? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????session.close( true );??
- ????}??
- ??
- ???? @Override ??
- ???? public ? void ?sessionCreated(IoSession?session)? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????System.out.println( "the?new?session?is?connecting" );??
- ????}??
- ??
- ???? @Override ??
- ???? public ? void ?messageReceived(IoSession?session,?Object?message)??
- ???????????? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????String?str?=?message.toString();??
- ????????System.out.println(str);??
- ????}??
- }??
最簡單的一個mina服務(wù)器實現(xiàn),telnet就可以看到效果。?
稍微解釋一下?
mina的實現(xiàn)主要在于給IoAcceptor增加過濾器?
new ProtocolCodecFilter(new TextLineCodecFactory()):這是一個解碼器,可以自己實現(xiàn)ProtocolCodecFactory接口編寫特定的解碼器。不過注意一下,解碼器必須和編碼器同時使用,服務(wù)器端實現(xiàn)特定解碼器的同時需要客戶端應(yīng)用特定編碼。?
常用過濾器還有日志LoggingFilter()等,具體可以查看api。?
這里筆者想說的是對于一般的socket服務(wù)器,可能客戶端并不會使用mina,譬如j2me或者干脆是一個c++的socket請求,此時我們上面的demo將毫無作用,具體來說就是解碼過濾器失效。在實際應(yīng)用中,我們一般是這樣的處理的:?
- public ? class ?Server? extends ?Thread?{??
- ???? private ? static ? final ? int ?PORT?=? 11001 ;??
- ??
- ???? public ? void ?run()?{??
- ????????IoAcceptor?acceptor?=? new ?NioSocketAcceptor();??
- ????????acceptor.setHandler( new ?TestHandler());??
- ????????????acceptor.getFilterChain().addLast( "ddd" ,? new ?StreamWriteFilter());??
- ????????acceptor.getSessionConfig().setReadBufferSize( 2048 );??
- ????????acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,? 10 );??
- ???????? try ?{??
- ????????????acceptor.bind( new ?InetSocketAddress(PORT));??
- ????????}? catch ?(IOException?e)?{??
- ???????????? //?TODO?Auto-generated?catch?block ??
- ????????????e.printStackTrace();??
- ????????}??
- ????????System.out.println( "The?ProjectServer?start?on?port:?" ?+?PORT);??
- ????}??
- ??
- ???? public ? static ? void ?main(String[]?args)?{??
- ????????Server?server?=? new ?Server();??
- ????????server.start();??
- ????}??
- }??
注意我們并沒有再使用 acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory())); ?
而是 acceptor.getFilterChain().addLast("ddd", new StreamWriteFilter()); ?
這個過濾器是直接對寫入流操作,即原始的數(shù)據(jù)流?
handler端代碼也要修改?
- public ? class ?TestHandler? extends ?IoHandlerAdapter?{??
- ??
- ???? @Override ??
- ???? public ? void ?exceptionCaught(IoSession?session,?Throwable?cause)??
- ???????????? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????session.close( true );??
- ????}??
- ??
- ???? @Override ??
- ???? public ? void ?sessionCreated(IoSession?session)? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ????????System.out.println( "the?new?session?is?connecting" );??
- ????}??
- ??
- ???? @Override ??
- ???? public ? void ?messageReceived(IoSession?session,?Object?message)??
- ???????????? throws ?Exception?{??
- ???????? //?TODO?Auto-generated?method?stub ??
- ??
- ????????IoBuffer?buffer=(IoBuffer)message;??
- ????????ByteBuffer?bf=?buffer.buf();??
- ???????? byte []?tempBuffer= new ? byte [bf.limit()];??
- ????????bf.get(tempBuffer);??
- ????????String?str= new ?String(tempBuffer);??
- ????????System.out.println(str);??
- ????}??
- }??
注意這段?
- IoBuffer?buffer=(IoBuffer)message;??
- ByteBuffer?bf=?buffer.buf();??
- byte []?tempBuffer= new ? byte [bf.limit()];??
- bf.get(tempBuffer);??
- String?str= new ?String(tempBuffer);??
- System.out.println(str);??
將原始的數(shù)據(jù)流還原為字符串,如果傳輸協(xié)議不是字符串而是byte數(shù)組,就直接對tempBuffer操作即可。?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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