Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中。目前正在使用 MINA 的软件包括有:Apache Directory Project、AsyncWeb、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。
客户端的主要逻辑思路:
1.客户端首先创建一个IOConnector 用来和服务端通信,顾名思义这就是建立的一个连接对象;
2.在这个连接上创建一个session, 客户端中的业务方法可以向session中写入数据,数据经过Filter Chain的过滤后会发送给服务端;
3.从服务端发回的数据也会首先经过Filter Chain的过滤,然后交给IOHandler做进一步的处理
mina整体结构图
Android客户端
1.(基于Android studio)首先在build.grade的dependencies下将mina包导入
compile 'org.apache.mina:mina-core:2.0.7'
2.直接上代码--核心代码
2.1
package com.eb.sc.tcprequest; import android.content.Context; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.eb.sc.MainActivity; import com.eb.sc.activity.SettingActivity; import com.eb.sc.bean.Params; import com.eb.sc.utils.AESCipher; import com.eb.sc.utils.BaseConfig; import com.eb.sc.utils.Constants; import com.eb.sc.utils.HexStr; import com.eb.sc.utils.NetWorkUtils; import com.eb.sc.utils.Utils; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.future.WriteFuture; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.LineDelimiter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.keepalive.KeepAliveFilter; import org.apache.mina.filter.keepalive.KeepAliveMessageFactory; import org.apache.mina.filter.keepalive.KeepAliveRequestTimeoutHandler; import org.apache.mina.filter.ssl.SslFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; import java.net.InetSocketAddress; import java.nio.charset.Charset; import static com.squareup.okhttp.internal.Internal.logger; public class PushManager { private static volatile PushManager manager; private static NioSocketConnector connector; private static ConnectFuture connectFuture; private static IoSession ioSession; private static Context mcontext; private ClientSessionHandler clientSessionHandler = null; public ClientSessionHandler getClientSessionHandler(String strs) { sendMessage(strs); return clientSessionHandler; } private PushManager(Context context){ connector = new NioSocketConnector(); connector.setConnectTimeoutMillis(Params.CONNECT_TIMEOUT); //---------------------- //为接收器设置管理服务 clientSessionHandler=new ClientSessionHandler(context); connector.setHandler(clientSessionHandler); //设置过滤器(使用Mina提供的文本换行符编解码器) // connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ByteArrayCodecFactory()));//重点 (服务器不是mina)改写文本编解码器 //读写通道5秒内无操作进入空闲状态 connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, Params.REQUEST_TIMEOUT); //设置读取数据的缓冲区大小 connector.getSessionConfig().setReadBufferSize(2048); //设置心跳 KeepAliveMessageFactory heartBeatFactory = new ClientKeepAliveMessageFactoryImp(context); KeepAliveRequestTimeoutHandler heartBeatHandler = new ClientKeepAliveMessageTimeoutFactoryImp(); KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory, IdleStatus.BOTH_IDLE,heartBeatHandler); //是否回发 heartBeat.setForwardEvent(true); //心跳发送频率 heartBeat.setRequestInterval(Params.REQUEST_INTERVAL); connector.getSessionConfig().setKeepAlive(true); connector.getFilterChain().addLast("keepalive", heartBeat); } public static PushManager getInstance(Context context){ mcontext=context; if (manager == null) { synchronized (PushManager.class) { manager = new PushManager(mcontext); } } return manager; } public void add(){ BaseConfig bg=new BaseConfig(mcontext); String dd=bg.getStringValue(Constants.tcp_ip,""); if(TextUtils.isEmpty(dd)){ manager=null; return ; } if(!NetWorkUtils.isNetworkConnected(mcontext)){ manager=null; return ; } BarAsyncTask task=new BarAsyncTask(); task.execute(); } /** * 连接 * @return */ public boolean connect() { BaseConfig bg=new BaseConfig(mcontext); if (connector != null && connector.isActive() && connectFuture != null && connectFuture.isConnected() && ioSession != null && ioSession.isConnected()) { return true; } try { connectFuture = connector.connect(new InetSocketAddress(bg.getStringValue(Constants.tcp_ip,""),Integer.parseInt(bg.getStringValue(Constants.ip_port,"")))); //等待是否连接成功,相当于是转异步执行为同步执行。 connectFuture.awaitUninterruptibly(); bg.setStringValue(Constants.havelink, "-1"); //连接成功后获取会话对象。如果没有上面的等待,由于connect()方法是异步的,session 可能会无法获取。 ioSession = connectFuture.getSession(); // String encrypt = AESCipher.encrypt(Params.KEY,); bg.setStringValue(Constants.havelink, "1"); sendMessage(Params.SHENGJI); String by= bg.getStringValue(Constants.px_list,""); if(TextUtils.isEmpty(by)){ sendMessage(Params.SHEBEI); } return true; } catch (Exception e){ e.printStackTrace(); new RelayTask().execute();//断线重连(我这里是3s主动链接) } return false; } class BarAsyncTask extends AsyncTask<Integer, Integer, String> { @Override protected String doInBackground(Integer... params) { connect(); return null; } @Override protected void onPostExecute(String result) { } } class RelayTask extends AsyncTask<Integer, Integer, String> { @Override protected String doInBackground(Integer... params) { try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } connect(); return null; } @Override protected void onPostExecute(String result) { } } /** * 关闭 */ public void close() { if (ioSession != null && ioSession.isConnected()){ ioSession.close(true); } if (connectFuture != null && connectFuture.isConnected()){ connectFuture.cancel(); } if (connector != null && !connector.isDisposed()) { connector.dispose(); } } /** * 发送 * @param message * @return */ public boolean sendMessage(String message) { if (ioSession == null || !ioSession.isConnected()) { return false; } WriteFuture writeFuture = ioSession.write(message); if (writeFuture == null) { return false; } writeFuture.awaitUninterruptibly(); if (writeFuture.isWritten()) { return true; } else { return false; } } }
2.2 心跳处理ClientKeepAliveMessageFactoryImp.class和ClientKeepAliveMessageTimeoutFactoryImp.class2.3.Mina的中业务逻辑处理都可以在IoHandler中package com.eb.sc.tcprequest; import android.content.Context; import android.text.TextUtils; import android.util.Log; import com.eb.sc.bean.Params; import com.eb.sc.utils.BaseConfig; import com.eb.sc.utils.Constants; import com.eb.sc.utils.Utils; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.keepalive.KeepAliveMessageFactory; /** * Created by lyj on 2017/7/28. */ public class ClientKeepAliveMessageFactoryImp implements KeepAliveMessageFactory { private Context context; public ClientKeepAliveMessageFactoryImp(Context context){ this.context=context; } @Override public boolean isRequest(IoSession ioSession, Object o) { if (o instanceof String && o.equals(Utils.xintiao(context))) { return true; } return false; } @Override public boolean isResponse(IoSession ioSession, Object o) { if (o instanceof String && o.equals(Utils.xintiao(context))) { return true; } return false; } @Override public Object getRequest(IoSession ioSession) { Log.e("lyj", "发送心跳..."); return Utils.xintiao(context); } @Override public Object getResponse(IoSession ioSession, Object o) { Log.e("lyj", "getResponse: "+o.toString()); return null; } }
package com.eb.sc.tcprequest; import android.util.Log; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.keepalive.KeepAliveFilter; import org.apache.mina.filter.keepalive.KeepAliveRequestTimeoutHandler; /** * Created by lyj on 2017/7/28. */ public class ClientKeepAliveMessageTimeoutFactoryImp implements KeepAliveRequestTimeoutHandler { @Override public void keepAliveRequestTimedOut(KeepAliveFilter keepAliveFilter, IoSession ioSession) throws Exception { Log.e("ClientKeepAliveMessageT", "心跳超时"); } }
package com.eb.sc.tcprequest; import android.content.Context; import android.content.Intent; import android.text.TextUtils; import android.util.Log; import com.eb.sc.bean.Params; import com.eb.sc.offline.OfflLineDataDb; import com.eb.sc.offline.ReceiveMsgService; import com.eb.sc.sdk.eventbus.ConnectEvent; import com.eb.sc.sdk.eventbus.GetOrderEvent; import com.eb.sc.sdk.eventbus.LoginEvent; import com.eb.sc.sdk.eventbus.PayResultEvent; import com.eb.sc.sdk.eventbus.PutEvent; import com.eb.sc.sdk.eventbus.QueryEvent; import com.eb.sc.sdk.eventbus.RefreshEvent; import com.eb.sc.sdk.eventbus.TongbuEvent; import com.eb.sc.sdk.eventbus.UpdateEvent; import com.eb.sc.utils.AESCipher; import com.eb.sc.utils.AnalysisHelp; import com.eb.sc.utils.BaseConfig; import com.eb.sc.utils.Constants; import com.eb.sc.utils.HexStr; import com.eb.sc.utils.Utils; import org.aisen.android.component.eventbus.NotificationCenter; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import java.nio.ByteBuffer; /** * Created by lyj on 2017/7/28. */ public class ClientSessionHandler extends IoHandlerAdapter { private Context mcontext; public ClientSessionHandler(Context context) { this.mcontext = context; } @Override public void sessionCreated(IoSession session) throws Exception { super.sessionCreated(session); Log.e("lyj", "服务器与客户端创建连接..."); } @Override public void sessionOpened(IoSession session) throws Exception { super.sessionOpened(session); Log.e("lyj", "服务器与客户端连接打开..."); } @Override public void sessionClosed(IoSession session) throws Exception { super.sessionClosed(session); Log.e("lyj", "服务器与客户端断开连接..."); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { super.exceptionCaught(session, cause); Log.e("lyj", "服务器发送异常..."); } @Override public void messageReceived(IoSession session, Object message) throws Exception { super.messageReceived(session, message); Log.e("lyj", "客户端接受消息成功..." + HexStr.hexStr2Str((message.toString()).substring(8, message.toString().length()))); } @Override public void messageSent(IoSession session, Object message) throws Exception { super.messageSent(session, message); Log.e("lyj", "客户端发送消息成功..." + message.toString()); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { super.sessionIdle(session, status); Log.e("lyj", "客户端进入空闲状态..."); } }
2.4:之前2.1代码里面提到过设置过滤器(改写文本换行符编码器)由于服务器不是mina,而且文本消息有点长,出现粘包现象,直接看下面代码:package com.eb.sc.tcprequest; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.codec.ProtocolDecoder; import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.textline.TextLineEncoder; /** * Created by Administrator on 2017/8/5. */ public class ByteArrayCodecFactory implements ProtocolCodecFactory { private ByteArrayDecoder decoder; private TextLineEncoder encoder; public ByteArrayCodecFactory() { encoder = new TextLineEncoder(); decoder = new ByteArrayDecoder(); } @Override public ProtocolDecoder getDecoder(IoSession session) throws Exception { return decoder; } @Override public ProtocolEncoder getEncoder(IoSession session) throws Exception { return encoder; } }
粘包处理如下,定义一个报头和消息结束符,
package com.eb.sc.tcprequest; import android.util.Log; import com.eb.sc.utils.HexStr; import com.eb.sc.utils.Utils; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.CumulativeProtocolDecoder; import org.apache.mina.filter.codec.ProtocolDecoderOutput; import java.nio.charset.Charset; /** * Created by LYJ on 2017/8/5. */ public class ByteArrayDecoder extends CumulativeProtocolDecoder { @Override protected boolean doDecode(IoSession session, IoBuffer buf, ProtocolDecoderOutput out) throws Exception { Log.i("ClientSessionHandler","remaining"+buf.remaining()); if(buf.remaining() > 0) { String getmsg = buf.getHexDump().toString().replace(" ", ""); buf.mark(); buf.reset(); if (getmsg.startsWith("4022")) { String getbody = HexStr.hexStr2Str((getmsg.toString()).substring(8, getmsg.toString().length())); if (getbody.length() < 114) { return false; }else { out.write(buf.getHexDump().toString().replace(" ", "")); Log.i("ClientSessionHandler", "IoBuffer1====" + buf.getHexDump().toString().replace(" ", "")); if(buf.remaining() > 0){ return true; } if (buf != null){ buf.flip(); } } }else if(getmsg.startsWith("4024")||getmsg.startsWith("4011")){ String getbody = HexStr.hexStr2Str((getmsg.toString()).substring(8, getmsg.toString().length())); if(!getbody.endsWith("&")){ return false;//结尾不是&继续读取 }else { String thisjieguo=buf.getHexDump().toString().replace(" ", ""); out.write(thisjieguo.substring(0,thisjieguo.length()-1)); Log.i("ClientSessionHandler", "IoBuffer1====" + buf.getHexDump().toString().replace(" ", "")); if(buf.remaining() > 0){ return true; } if (buf != null) { buf.flip(); } } }else { out.write(buf.getHexDump().toString().replace(" ", "")); Log.i("ClientSessionHandler", "IoBuffer2====" + buf.getHexDump().toString().replace(" ", "")); if(buf.remaining() > 0){ return true; } if (buf != null) { buf.flip(); } } } return false;//处理成功,让父类进行接收下个包 } }
到此可以用mina框架对接自己的业务了