http://unbounder.iteye.com/blog/481396
http://unbounder.iteye.com/blog/481668
?
?
基于io包的阻塞式socket通信代碼簡單,在連接數很少的情況下是一個不錯的選擇。不過實際應用中一個socket服務器采用傳統的阻塞式socket方式通信可能會是一場災難,一路socket同時進行讀寫操作可能就需要兩條線程,如果需要并發一百路socket(這個量其實很小了),可能就是兩百條線程,大概幾分鐘后cpu占用率就是高居不下了。?
基于原生nio的socket通信時一種很好的解決方案,基于事件的通知模式使得多并發時不用維持高數量的線程,高并發的socket服務器的java實現成為現實。不過原生nio代碼十分復雜,無論編寫還是修改都是一件頭疼的事。“屏蔽底層的繁瑣工作,讓程序員將注意力集中于業務邏輯本身”,有需求就有生產力進步,于是各式各樣的nio框架涌現而出,而筆者使用到的是其中最常見的兩種:xSocket和MINA。?
1 xsocket框架
?
官網:http://xsocket.sourceforge.net/?
xSocket是一套非常簡潔的nio框架,利用這套框架,你可以在完全不了解nio的情況下設計出高并發的socket服務器。?
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();??
- ????????}??
- ???????? //服務器運行 ??
- ????????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 ;??
- ????}??
- ??
- ???? /*?處理接受數據事件?*/ ??
- ???? @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"為間隔讀取數據,xSocket還提供按照長度讀取以及全部讀取到一個ByteBuffer幾種讀取數據的方式,如果傳遞過程中采取byte數組格式,在接受數據時還可以使用xSocket自帶的工具類DataConverter進行轉化。譬如接收數據時如此操作:?
- ByteBuffer?copyBuffer?=?ByteBuffer.allocate( 20000 );??
- nbc.read(copyBuffer);??
- copyBuffer.flip();??
- String?str?=?DataConverter.toString(copyBuffer,? "utf-8" );??
就可以將二進制格式的傳輸內容還原為原始字符串了。?
需要說明的是雖然xSocket代碼簡單、開發快捷,但是由于太“上層”了,所以很多地方不利于coder自己控制。譬如socket通信中一個很常見的“半包連包”問題用xsocket處理起來就會很麻煩,而且如果傳輸協議是經過特殊設計的,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服務器實現,telnet就可以看到效果。?
稍微解釋一下?
mina的實現主要在于給IoAcceptor增加過濾器?
new ProtocolCodecFilter(new TextLineCodecFactory()):這是一個解碼器,可以自己實現ProtocolCodecFactory接口編寫特定的解碼器。不過注意一下,解碼器必須和編碼器同時使用,服務器端實現特定解碼器的同時需要客戶端應用特定編碼。?
常用過濾器還有日志LoggingFilter()等,具體可以查看api。?
這里筆者想說的是對于一般的socket服務器,可能客戶端并不會使用mina,譬如j2me或者干脆是一個c++的socket請求,此時我們上面的demo將毫無作用,具體來說就是解碼過濾器失效。在實際應用中,我們一般是這樣的處理的:?
- 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()); ?
這個過濾器是直接對寫入流操作,即原始的數據流?
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);??
將原始的數據流還原為字符串,如果傳輸協議不是字符串而是byte數組,就直接對tempBuffer操作即可。?
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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