Mina学习(4):实现自定义编解码器并解决半包,丢包,粘包问题

一、编码器:将要发送的数据转化成byte[] 进行传输

  • 自定义编码器的实现:
package filter;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import protocal.ProtocalPackage;

public class TestEnCoder extends ProtocolEncoderAdapter{
    //用于打印日志信息
    private final static Logger log = LoggerFactory
            .getLogger(ProtocalEncoder.class);
    //编码 将数据包转成字节数组
    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
         ProtocalPackage value = (ProtocalPackage) message;
            //根据报文长度开辟空间
            IoBuffer buff = IoBuffer.allocate(value.getLength());
            //设置为可自动扩展空间
            buff.setAutoExpand(true);
            //将报文中的信息添加到buff中
            buff.putInt(value.getLength());
            if(value.getContent() != null) {
                buff.put(value.getContent().getBytes());
            }
            buff.flip();
            //将报文发送出去
            out.write(buff);
    }
}

二、解码器:将收到的byte[] 按照协议还原成数据

  • 解码器需要解决的问题:网络通信中经常会遇到数据包丢失(丢包),数据包残缺(半包),还有粘包问题。

  • 具体实现代码,并解决上述问题:

package filter;

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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import protocal.DeviceDataProtocal;
import protocal.HeartbeatProtocal;
import protocal.LoginRequestProtocol;
import protocal.LoginResponseProtocal;

public class TestDecoder extends CumulativeProtocolDecoder{
    //打印日志信息
    private final static Logger log = LoggerFactory
            .getLogger(ProtocalDecoder.class);

    @Override
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        int packHeadLenth = 4;  //包头长度(int 的长度) 根据自定义协议的包头的长度
        if(in.remaining() > packHeadLenth){  //说明缓冲区中有数据
            in.mark();//标记当前position,以便后继的reset操作能恢复position位置

            //获取数据包长度
            int len = in.getInt();
            log.info("len = "+len);

            //上面的get会改变remaining()的值

            if(in.remaining() //内容不够, 重置position到操作前,进行下一轮接受新数据
                in.reset();
                return false;
            }else{
                //内容足够
                in.reset(); //重置回复position位置到操作前
                byte[] packArray = new byte[len]; 
                in.get(packArray, 0, len); //获取整条报文

                //根据自己需要解析接收到的东西  我的例子 把收到的报文转成String
                String str = new String(packArray);
                out.write(str); //发送出去 就算完成了

                if(in.remaining() > 0){//如果读取一个完整包内容后还粘了包,就让父类再调用一次,进行下一次解析
                    return true;
                }
            }
        }
        return false;  //处理成功
    }
}

三、实现编解码器工厂类:

package filter;

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;

public class TestFactory implements ProtocolCodecFactory{
    private final TestDecoder decoder;
    private final TestEnCoder encoder;
        //构造
        public TestFactory() {
            encoder = new TestEnCoder();
            decoder = new TestDecoder();
        }

    @Override
    public ProtocolDecoder getDecoder(IoSession arg0) throws Exception {
        // TODO Auto-generated method stub
        return decoder;
    }

    @Override
    public ProtocolEncoder getEncoder(IoSession arg0) throws Exception {
        // TODO Auto-generated method stub
        return encoder;
    }
}

四、在服务端和客户端中的调用:

//connector 是 IoConnector 的对象
connector.getFilterChain().addLast("coderc", new ProtocolCodecFilter(
                new TestFactory() ));

你可能感兴趣的:(mina2.0)