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做进一步的处理
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 {
@Override
protected String doInBackground(Integer... params) {
connect();
return null;
}
@Override
protected void onPostExecute(String result) {
}
}
class RelayTask extends AsyncTask {
@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.class
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框架对接自己的业务了