Mina框架使用---Android客户端的实现,断线重连,粘包处理(服务端非mina)

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.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", "心跳超时");
    }
}

2.3.Mina的中业务逻辑处理都可以在IoHandler中
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框架对接自己的业务了吐舌头
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

揽m月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值