Android mina长连接 (三)

本文介绍了一个基于Android的Mina客户端实现案例,详细展示了如何使用Mina框架进行网络通信,包括连接配置、消息收发、心跳检测及异常处理等核心功能。

第二篇帖子中 没有心跳  断线从连 

我的第一次使用

Android客户端

MinaClient.java

package com.wjbzg.www.ernie_yaojiangji;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;

import com.google.gson.Gson;
import com.wjbzg.www.connect.CustomPack;
import com.wjbzg.www.connect.CustomProtocolCodecFactory;
import com.wjbzg.www.fragment.IoSessionGet;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
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.transport.socket.nio.NioSocketConnector;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import utils.Base64Utils;

public class Home3Activity extends AppCompatActivity {
    private static final String MINA_HOST = "192.168.1.7";
    private static final int MINA_PORT = 7777;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home3);
        openSession();
    }
    private void openSession() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 获取当前系统时间戳
                long start = System.currentTimeMillis();
                // 创建一个非阻塞的客户端
                IoConnector connector = new NioSocketConnector();
                // 设置编码过滤器
                connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new CustomProtocolCodecFactory(Charset.forName("UTF-8"))));
                // 设置缓冲区大小
                connector.getSessionConfig().setReadBufferSize(1024);
                // 设置空闲时间
                connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
                // 绑定逻辑处理Handler
                connector.setHandler(new MyClientHandler());
                // 创建连接
                ConnectFuture future = connector.connect(new InetSocketAddress(MINA_HOST, MINA_PORT));
                // 这里采用监听方式获取session
                future.addListener(new IoFutureListener<IoFuture>() {
                    // 当连接创建完成
                    public void operationComplete(IoFuture future) {
                        IoSession session = future.getSession();
//                        sendData(session);
                        Log.e("Tag","----------------------------测试数据准备发送-----------------------------");
                        String content = "{'op':'init','m_id':'1'}";
                        String enToStr = Base64.encodeToString(content.getBytes(), Base64.NO_WRAP);
                        try {
                            Log.e("Tag","测试数据 加密后"+enToStr);
                            CustomPack pack = new CustomPack((byte) 1, enToStr);
                            session.write(pack);
                            //将ioSession 设置成单例的,方便其他地方获取
                            IoSessionGet ioSessionGet = new IoSessionGet();
                            ioSessionGet.setIo(session);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        Log.e("Tag","----------------------------测试数据发送完毕-----------------------------");
                    }
                });
            }
        }).start();

    }

    class MyClientHandler extends IoHandlerAdapter {

        private  final Logger logger = LoggerFactory.getLogger(MyClientHandler.class);

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            Gson gson = new Gson();
            CustomPack pack = (CustomPack) message;
            String content = pack.getContent();
            //解密
            String decodedString = Base64Utils.getFromBase64(content);
//            String decodedString =new String(Base64.decode(content,Base64.DEFAULT));
//            Log.e("Tag","客户端接收消息成功:" + decodedString);
            JSONObject jsonObject = new JSONObject(decodedString);
            String op = jsonObject.getString("op");
            List goods = new ArrayList<>();
            if (op.equals("good_list")){
                //商品列表
                // {"op":"good_list",
                // "data":{"list":[{"good_id":1,"good_name":"森海耳机mx500"},{"good_id":2,"good_name":"兰蔻1x 口红"},{"good_id":3,"good_name":"zipoo打火机"},{"good_id":4,"good_name":"iphone xs 64G"},{"good_id":5,"good_name":"普通耳机"},{"good_id":6,"good_name":"纪念币"},{"good_id":7,"good_name":"鼠标wifi"},{"good_id":8,"good_name":"蓝牙耳机"},{"good_id":9,"good_name":"钢笔英雄"},{"good_id":10,"good_name":"漫步者mp3"},{"good_id":11,"good_name":"金士顿 U盘"}]},
                // "state":"1",
                // "message":"获取商品列表成功"}
                Log.e("Tag","客户端接收消息成功,商品列表:" + decodedString);
                Intent intent = new Intent();
                intent.setAction("com.example.administrator.myapplication.MY_BROADCAST");
                intent.putExtra("name", decodedString);
                sendBroadcast(intent);
            }else if (op.equals("init")){
                //欢迎你
            }else if (op.equals("good_state")){
                //商品状态
                //{"op":"good_state",
                // "data":{"chanelList":[{"id":1,"m_id":1,"stock":5,"state":1,"position":1,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":2,"m_id":1,"stock":5,"state":1,"position":2,"good_id":1,"winning_rate":50,"good_name":"口红"},{"id":3,"m_id":1,"stock":5,"state":1,"position":3,"good_id":1,"winning_rate":50,"good_name":"钢笔"},{"id":4,"m_id":1,"stock":0,"state":1,"position":4,"good_id":1,"winning_rate":50,"good_name":"U盘"},{"id":5,"m_id":1,"stock":0,"state":1,"position":5,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":6,"m_id":1,"stock":0,"state":1,"position":6,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":7,"m_id":1,"stock":0,"state":1,"position":7,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":8,"m_id":1,"stock":0,"state":1,"position":8,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":9,"m_id":1,"stock":0,"state":1,"position":9,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":10,"m_id":1,"stock":0,"state":1,"position":10,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":11,"m_id":1,"stock":0,"state":1,"position":11,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":12,"m_id":1,"stock":0,"state":1,"position":12,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":13,"m_id":1,"stock":0,"state":1,"position":13,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":14,"m_id":1,"stock":0,"state":1,"position":14,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":15,"m_id":1,"stock":0,"state":1,"position":15,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":16,"m_id":1,"stock":0,"state":1,"position":16,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":17,"m_id":1,"stock":0,"state":1,"position":17,"good_id":1,"winning_rate":50,"good_name":"蓝牙耳机"},{"id":18,"m_id":1,"stock":5,"state":1,"position":18,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":19,"m_id":1,"stock":5,"state":1,"position":19,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":20,"m_id":1,"stock":5,"state":1,"position":20,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":21,"m_id":1,"stock":5,"state":1,"position":21,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":22,"m_id":1,"stock":5,"state":1,"position":22,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":23,"m_id":1,"stock":5,"state":1,"position":23,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":24,"m_id":1,"stock":5,"state":1,"position":24,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":25,"m_id":1,"stock":5,"state":1,"position":25,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":26,"m_id":1,"stock":5,"state":1,"position":26,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":27,"m_id":1,"stock":5,"state":1,"position":27,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":28,"m_id":1,"stock":5,"state":1,"position":29,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":29,"m_id":1,"stock":5,"state":1,"position":29,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":30,"m_id":1,"stock":5,"state":1,"position":30,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":31,"m_id":1,"stock":5,"state":1,"position":31,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":32,"m_id":1,"stock":5,"state":1,"position":32,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":33,"m_id":1,"stock":5,"state":1,"position":33,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":34,"m_id":1,"stock":5,"state":1,"position":34,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":35,"m_id":1,"stock":5,"state":1,"position":35,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":36,"m_id":1,"stock":5,"state":1,"position":36,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":37,"m_id":1,"stock":5,"state":1,"position":37,"good_id":1,"winning_rate":50,"good_name":"耳机"},{"id":38,"m_id":1,"stock":5,"state":1,"position":38,"good_id":1,"winning_rate":50,"goo
                Log.e("Tag","客户端接收消息成功,商品状态:" + decodedString);
                Intent intent = new Intent();
                intent.setAction("com.example.administrator.myapplication.GoodsStateBROADCAST");
                intent.putExtra("good_state",decodedString);
                sendBroadcast(intent);
            }else if (op.equals("open ")){
                //抽奖
                String state = jsonObject.getString("state");
                if (state.equals("1")){
                    String postion = jsonObject.getString("postion");
                    int p = Integer.parseInt(postion);
                    String number_HEX = Integer.toHexString(p);
                    String data =  "FA FE 01 "+number_HEX+" 01 CC DD DF";

//                    serialPortUtils.sendSerialPort3(data);
//                openSendData(session,postion);
                    Log.e("Tag","----------------------------测试数据准备发送-----------------------------");
                    // 模拟发送100次数据
                    for (int i = 0; i < 1; i++) {
                        //String content = "nnnn:" + i;
                        String contents = "{'op':'init','postion':"+postion+",'state':'1','message':'转动成功','m_id':'1'}";
                        String enToStr = Base64.encodeToString(contents.getBytes(), Base64.NO_WRAP);
                        try {
                            Log.e("TAG",""+enToStr);
                            Log.e("Tag","测试数据 加密后"+enToStr);
                            CustomPack pack2 = new CustomPack((byte) i, enToStr);
                            session.write(pack2);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    Log.e("Tag","----------------------------测试数据发送完毕-----------------------------");
                }
            }
        }

        @Override
        public void messageSent(IoSession session, Object message) throws Exception {
            CustomPack pack = (CustomPack) message;
            Log.e("Tag","客户端发送消息成功:" + pack);

        }

        @Override
        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
            Log.e("Tag","客户端处理消息异常:" + cause.getMessage());
        }

        @Override
        public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
            // 空闲时,关闭session
            if (status == IdleStatus.BOTH_IDLE) {
                Log.e("Tag","session进入空闲,准备关闭session");
                //session.closeNow();
            }
        }
    }
}

CustomPack.java

package com.fudai.mina.protocol2;

/*
    自定义协议包
    协议为– 数据长度(4个字节)+ 协议编号(1字节)+ 真实数据。
*/
public class CustomPack {
 
    /**
     * 0x00表示请求
     */
    public static final byte REQUEST = 0x00;
    /**
     * 0x01表示回复
     */
    public static final byte RESPONSE = 0x01;
 
    // 总长度(编号字节 + 长度的字节 + 包体长度字节)
    private int len;
    // 版本号
    private byte flag;
    // 发送人,只是服务端-客户端,暂时无需发送人 接收人
    // private long sender;
    // 接收人
    // private long receiver;
    // 包体
    private String content;
 
    // 构造方法设置协议
    public CustomPack(byte flag, String content) {
        this.flag = flag;
        this.content = content;
        // 版本类型的长度1个字节, len的长度4个字节, 内容的字节数
        this.len = 1 + 4 + (content == null ? 0 : content.getBytes().length);
    }
 
    public int getLen() {
        return len;
    }
 
    public void setLen(int len) {
        this.len = len;
    }
 
    public byte getFlag() {
        return flag;
    }
 
    public void setFlag(byte flag) {
        this.flag = flag;
    }
 
    public String getContent() {
        return content;
    }
 
    public void setContent(String content) {
        this.content = content;
    }
 
    @Override
    public String toString() {
        return "CustomPack{" +
                "len=" + len +
                ", flag=" + flag +
                ", content='" + content + '\'' +
                '}';
    }
}

这是我第一次使用的, 后来经过查阅资料,才明白要把 mina代码写在服务Service中

CustomProtocolCodecFactory.java
package com.wjbzg.www.connect;

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 java.nio.charset.Charset;


/*
    自定义编解码工厂类
*/
public class CustomProtocolCodecFactory implements ProtocolCodecFactory {
 
    private final ProtocolEncoder encoder;
    private final ProtocolDecoder decoder;
 
    public CustomProtocolCodecFactory() {
        this(Charset.forName("UTF-8"));
    }
 
    // 构造方法注入编解码器
    public CustomProtocolCodecFactory(Charset charset) {
        this.encoder = new CustomProtocolEncoder(charset);
        this.decoder = new CustomProtocolDecoder(charset);
    }
 
    public ProtocolEncoder getEncoder(IoSession session) throws Exception {
        return encoder;
    }
 
    public ProtocolDecoder getDecoder(IoSession session) throws Exception {
        return decoder;
    }
}
CustomProtocolDecoder.java
package com.wjbzg.www.connect;

import android.util.Base64;
import android.util.Log;

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;

/*
 *
 * describe 自定义解码器
 * @author xmc
 * @param  * @param null
 * @return
 */
public class CustomProtocolDecoder extends CumulativeProtocolDecoder {
 
    private final Charset charset;
 
    public CustomProtocolDecoder() {
        this.charset = Charset.defaultCharset();
    }
 
    // 构造方法注入编码格式
    public CustomProtocolDecoder(Charset charset) {
        this.charset = charset;
    }
 
    @Override
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        // 包头的长度
        final int PACK_HEAD_LEN = 5;
        // 拆包时,如果可读数据的长度小于包头的长度,就不进行读取
        if (in.remaining() < PACK_HEAD_LEN) {
            System.out.println("可读数据的长度小于包头的长度");
            Log.e("Tag","可读数据的长度小于包头的长度");
            return false;
        }
        if (in.remaining() > 1) {
            // 标记设为当前
            in.mark();
            // 获取总长度
            int length = in.getInt(in.position());
            // 如果可读取数据的长度 小于 总长度 - 包头的长度 ,则结束拆包,等待下一次
            if (in.remaining() < (length - PACK_HEAD_LEN)) {
                System.out.println("可读取数据的长度 小于 总长度"+length);
                Log.e("Tag","可读取数据的长度 小于 总长度"+length);
                in.reset();
                return false;
            } else {
                System.out.println("读取一条完整记录");
                Log.e("Tag","读取一条完整记录");
                // 重置,并读取一条完整记录
                in.reset();
                byte[] bytes = new byte[length];
                // 获取长度4个字节、版本1个字节、内容
                in.get(bytes, 0, length);
                byte flag = bytes[4];

                String content = new String(bytes, PACK_HEAD_LEN, length - PACK_HEAD_LEN, charset);
                //64解码
//                String contentBase64 = new String(Base64.encode(content.getBytes("utf-8"), Base64.NO_WRAP),"utf-8");
                // 封装为自定义的java对象
                CustomPack pack = new CustomPack(flag,content);
                out.write(pack);

                //测试读取json 对象转Map
                /*JSONObject  jsonObject = JSONObject.parseObject(pack.getContent());
                Map<String,Object> map = jsonObject;
                System.out.println("map对象是:" + map);
                Object object = map.get("message");
                System.out.println("message的值是:"+object);*/

                // 如果读取一条记录后,还存在数据(粘包),则再次进行调用
                return in.remaining() > 0;
            }
        }
        return false;
    }
}
CustomProtocolEncoder.java
package com.wjbzg.www.connect;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

import java.nio.charset.Charset;

/*
 *
 * describe 自定义编码器
 * @author xmc
 * @param  * @param null
 * @return
 */
public class CustomProtocolEncoder implements ProtocolEncoder {
 
    private final Charset charset;
 
    public CustomProtocolEncoder() {
        this.charset = Charset.defaultCharset();
    }
 
    // 构造方法注入编码格式
    public CustomProtocolEncoder(Charset charset) {
        this.charset = charset;
    }
 
    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        // 转为自定义协议包
        CustomPack customPack = (CustomPack) message;
        // 初始化缓冲区
        IoBuffer buffer = IoBuffer.allocate(customPack.getLen())
                                  .setAutoExpand(true);
        // 设置长度、报头、内容
        buffer.putInt(customPack.getLen());
        buffer.put(customPack.getFlag());
        if (customPack.getContent() != null) {
            buffer.put(customPack.getContent().getBytes());
        }
        // 重置mask,发送buffer
        buffer.flip();
        out.write(buffer);
    }
 
    @Override
    public void dispose(IoSession session) throws Exception {
 
    }
}

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值