mina現在用的很多了,之前也有用到,但是畢竟不熟悉,於是查了一些資料,做了一些總結。看代碼是最直觀的,比什么長篇大論都要好。不過其中重要的理論,也要理解下。
首先是環境,程序運行需要幾個包,這里用maven比較方便。
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>MyMinaServer</groupId> <artifactId>mina</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>mina</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> <version>2.0.4</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
然后就可以寫代碼了。
---------------------------------------------------------- 分割線 -----------------------------------------------------------------------------------------
一,簡單的客戶端和服務端程序
服務端程序:
package MyMinaServer.mina; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MainServer { private static final int Port=8888; public static void main(String[] args) { IoAcceptor ioAcceptor=new NioSocketAcceptor(); System.out.println("begin server...."); ioAcceptor.getFilterChain().addLast("logger", new LoggingFilter()); ioAcceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); ioAcceptor.setHandler(new HelloWorldHandler()); ioAcceptor.getSessionConfig().setReadBufferSize(2048); ioAcceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); try { ioAcceptor.bind(new InetSocketAddress(Port)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
服務端,創建連接,然后這里注冊了幾個過濾鏈,這里簡單寫了需要的兩個,到后面的內容中還可以加入一個加密的ssl鏈。
其中重要的是setHandler這個,在這個里面,我們可以定義自己的handler,然后做自己的業務。下面有這個handler的簡單代碼。
最后設置session的緩沖,和idle(空閑處理)的設定,再為此連接綁定一個端口。
其中需要注意的是,在服務端和客戶端的代碼里面,如果要傳遞string信息,codec編碼過濾器中,要這么寫:new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))。否則報錯。
業務處理的handler:
package MyMinaServer.mina; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; public class HelloWorldHandler extends IoHandlerAdapter{ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { // TODO Auto-generated method stub super.exceptionCaught(session, cause); } @Override public void messageReceived(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub String string=message.toString(); if (string.trim().equalsIgnoreCase("quit")) { session.close(true); return; } System.out.println("recevied message:"+string); String reply=" hi, i am server"; session.write(reply); System.out.println("message have been written"); } @Override public void messageSent(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub System.out.println("message have been sent"); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub System.out.println("closed session"); } @Override public void sessionCreated(IoSession session) throws Exception { // TODO Auto-generated method stub System.out.println("session created"); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { // TODO Auto-generated method stub System.out.println("IDLE "+session.getIdleCount(status)); } @Override public void sessionOpened(IoSession session) throws Exception { // TODO Auto-generated method stub System.out.println("session opened"); } }
這里的每個方法,算是事件。在每個事件中,我們可以定義自己的處理。在前面的《AndroidPn源碼分析(一)》這篇文章中,筆者曾寫過他們的每個事件代表的含義,不過從字面意思也很好理解。
這里主要是對接受到信息的處理,如果不是收到quit字符,則返回給客戶端一句 hi,i am server。
服務端暫時到此,以下是客戶端。
package com.example.mina.server; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.future.CloseFuture; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter;import org.apache.mina.transport.socket.SocketConnector; import org.apache.mina.transport.socket.nio.NioSocketConnector; import com.example.mina.charset.CharsetFactory; import com.example.mina.hanlder.MsgHanler;public class MinaClient { private SocketConnector connector; private ConnectFuture future; private IoSession session; public boolean connect() { /* * 1.創建一個socket連接,連接到服務器 */ connector = new NioSocketConnector(); /* * 獲取過濾器鏈,用於添加過濾器 */ DefaultIoFilterChainBuilder chain = connector.getFilterChain(); // b.添加日志過濾器 chain.addLast("logger", new LoggingFilter()); // c.添加字符的編碼過濾器 chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8")))); /* * 3.設置消息處理器,用於處理接收到的消息 */ connector.setHandler(new MsgHanler()); /* * 4.根據IP和端口號連接到服務器 */ future = connector.connect(new InetSocketAddress("127.0.0.1", 8888)); // 等待連接創建完成 future.awaitUninterruptibly(); /* * 5.獲取session對象,通過session可以向服務器發送消息; */ session = future.getSession(); session.getConfig().setUseReadOperation(true); return future.isConnected(); } /** * 往服務器發送消息 * * @param message */ public void sendMsg2Server(String message) { session.write(message); } /** * 關閉與服務器的連接 * * @return */ public boolean close() { CloseFuture future = session.getCloseFuture(); future.awaitUninterruptibly(1000); connector.dispose(); return true; } }
然后同樣是一個客戶端的handler,和server的很像:
package com.example.mina.hanlder; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MsgHanler extends IoHandlerAdapter { private static final Logger log = LoggerFactory.getLogger(MsgHanler.class); @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { // 出現異常 log.error("--------exception--------"); super.exceptionCaught(session, cause); } @Override public void messageReceived(IoSession session, Object message) throws Exception { // 從服務器中接收到消息后的處理 log.info("--------msg receive--------"); log.info("Message:{}" + message.toString()); super.messageReceived(session, message); } @Override public void messageSent(IoSession session, Object message) throws Exception { // 往服務器中發送消息 log.info("--------msg sent--------"); super.messageSent(session, message); } @Override public void sessionCreated(IoSession session) throws Exception { // 當session被創建的時候調用 log.info("--------session create--------"); super.sessionCreated(session); } }
寫一個入口方法:
package com.example.mina.server; public class Main { public static void main(String[] args) { MinaClient client=new MinaClient(); client.connect(); client.sendMsg2Server("message from cilent"); } }
這樣,客戶端就可以工作了。
先啟動服務端,然后啟動客戶端,就可以看到在兩個控制台中,分別有交互信息。
服務端:
begin server....
session created
session opened
recevied message:message from cilent
message have been written
message have been sent
客戶端:
[QC] INFO [NioProcessor-2] org.apache.mina.filter.logging.LoggingFilter.log(186) | CREATED
[QC] INFO [NioProcessor-2] com.example.mina.hanlder.MsgHanler.sessionCreated(54) | --------session create--------
[QC] INFO [NioProcessor-2] org.apache.mina.filter.logging.LoggingFilter.log(186) | OPENED
[QC] INFO [NioProcessor-2] org.apache.mina.filter.logging.LoggingFilter.log(157) | SENT: HeapBuffer[pos=0 lim=0 cap=0: empty]
[QC] INFO [NioProcessor-2] com.example.mina.hanlder.MsgHanler.messageSent(47) | --------msg sent--------
[QC] INFO [NioProcessor-2] org.apache.mina.filter.logging.LoggingFilter.log(157) | RECEIVED: HeapBuffer[pos=0 lim=17 cap=2048: 20 68 69 2C 20 69 20 61 6D 20 73 65 72 76 65 72...]
[QC] INFO [NioProcessor-2] com.example.mina.hanlder.MsgHanler.messageReceived(39) | --------msg receive--------
[QC] INFO [NioProcessor-2] com.example.mina.hanlder.MsgHanler.messageReceived(40) | Message:{} hi, i am server
客戶端因為用了log4j,打印比較多信息。
到此,這個例子就完成了。