解决apache mina在网络环境慢下的粘包问题

package com.mfq.business;

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

import com.mfq.utils.ArrayUtils;
import com.mfq.utils.CodeUtil;

public class BusinessDecode extends ProtocolDecoderAdapter{
    private Logger log = Logger.getLogger(getClass());
    private static final int HEAD_BODY_LENGTH = 6;
    @Override
    public void decode(IoSession session, IoBuffer buffer, ProtocolDecoderOutput out)
            throws Exception {
        try{
            if(!buffer.hasRemaining()) return;
            /** 报文体长度不足时用户回置 */
            buffer.mark();
            /** 获取缓存 */
            MyByteBuffer myBuffer = (MyByteBuffer)session.getAttribute(CodeUtil.IOSESSION_BUFFER);
            if(myBuffer!=null){
                /** 首先尝试发送数据 */
                myBuffer.sendPack(out);
                /** 缓存包刚好发送完成则清除缓存包 */
                if(myBuffer.getBufferLeng()==0){
                    session.removeAttribute(CodeUtil.IOSESSION_BUFFER);
                    myBuffer = null;
                }
            }
            if(myBuffer!=null&&myBuffer.getBufferLeng()>0){//拥有缓存的情况下处理
                /** 自定义缓存字节长度 */
                int myLength = myBuffer.getBufferLeng();
                /** 先获取缓冲字节 */
                byte[] myByte = myBuffer.getBuffer();
                int bodyLength = 0;
                if(myLength >= HEAD_BODY_LENGTH){//缓存数据大于定义报文头长度下处理
                    int len = Integer.parseInt(new String(myByte, 0, HEAD_BODY_LENGTH), 10);
                    /** 获取剩余报文长度 */
                    bodyLength = len - myLength + HEAD_BODY_LENGTH;
                }else{
                    byte[] headPart = new byte[HEAD_BODY_LENGTH - myLength];
                    if(buffer.remaining()<headPart.length){
                        setByteBuffer(buffer, session, myBuffer, out);
                        return;
                    }
                    buffer.get(headPart);
                    /** 合并头 */
                    byte[] head = ArrayUtils.concatByteArray(myByte, headPart);
                    bodyLength = Integer.parseInt(new String(head));
                }
                if(bodyLength>buffer.remaining()){//报文体长度不足时处理
                    buffer.reset();
                    setByteBuffer(buffer, session, myBuffer, out);
                }else{//报文体长度合适处理
                    byte[] bodyPart = new byte[bodyLength];
                    buffer.get(bodyPart);
                    /** 合并两个包 */
                    out.write(new BusinessBean(ArrayUtils.concatByteArray(myByte, bodyPart)));
                    /** 刷新缓存 */
                    myBuffer.clearBuffer();
                    /** 判断是否还有字节,有则缓存。没有则移除session属性 */
                    if(buffer.hasRemaining()){
                        myBuffer.put(buffer);
                    }else{
                        session.removeAttribute(CodeUtil.IOSESSION_BUFFER);
                    }
                }
            }else{//无缓存情况下直接读取数据
                byte[] length = new byte[HEAD_BODY_LENGTH];
                /** 获取要读取的报文长度 */
                buffer.get(length);
                int len = Integer.parseInt(new String(length), 10);
                buffer.reset();
                /** 报文长度大于实际长度 */
                if(len+HEAD_BODY_LENGTH > buffer.remaining()){
                    setByteBuffer(buffer, session, null, out);
                }else{
                    byte[] body = new byte[len+HEAD_BODY_LENGTH];
                    buffer.get(body);
                    out.write(new BusinessBean(body));
                    if(buffer.hasRemaining()){
                        setByteBuffer(buffer, session, null, out);
                    }
                }
            }
            
        }catch(Exception e){
            out.write(new BusinessBean());
            log.error("分析报文异常", e);
            throw e;
        }
    }
    
    /**
     * 设置缓存
     * @author     littlehow
     * @createtime 2015-7-3 下午5:00:41
     *
     * @param buffer
     * @param session
     * @param myBuffer
     */
    private void setByteBuffer(IoBuffer buffer, IoSession session, MyByteBuffer myBuffer, ProtocolDecoderOutput out){
        if(!buffer.hasRemaining()) return;
        if(myBuffer==null){
            myBuffer = new MyByteBuffer();
            session.setAttribute(CodeUtil.IOSESSION_BUFFER, myBuffer);
        }
        myBuffer.put(buffer);
        /** 尝试发送 */
        myBuffer.sendPack(out);
    }
    
    /**
     * 缓存管理类
     * @author     littlehow
     * @createtime 2015-7-3 下午3:46:14
     *
     */
    static final class MyByteBuffer{
        private Logger log = Logger.getLogger(getClass());
        /** 缓存字节数的长度 */
        private int bufferLeng = 0;
        /** 缓存字节 */
        private byte[] bts = null;
        public MyByteBuffer(){
            super();
        }
        public int getBufferLeng() {
            return bufferLeng;
        }
        public byte[] getBuffer() {
            return bts;
        }
        public void put(IoBuffer buffer){
            try{
                if(bufferLeng==0){
                    bufferLeng = buffer.remaining();
                    bts = new byte[bufferLeng];
                    buffer.get(bts);
                }else{
                    bufferLeng += buffer.remaining();
                    byte[] tmp = new byte[buffer.remaining()];
                    buffer.get(tmp);
                    bts = ArrayUtils.concatByteArray(bts, tmp);
                }
            }catch(Exception e){
                log.error("设置缓存数据异常", e);
            }
            buffer.clear();
            buffer.flip();
        }
        
        
        public void clearBuffer(){
            this.bts = null;
            this.bufferLeng = 0;
        }
        
        /**
         * 缓存数据大于一个包时发送
         * @author     littlehow
         * @createtime 2015-7-3 下午5:09:13
         *
         * @param out
         */
        public void sendPack(ProtocolDecoderOutput out){
            if(this.bufferLeng==0) return;
            while(send(out)){}
        }
        
        /**
         * 真是发送信息
         * @author     littlehow
         * @createtime 2015-7-3 下午5:13:21
         *
         * @param out
         * @return
         */
        private boolean send(ProtocolDecoderOutput out){
            if(bufferLeng < HEAD_BODY_LENGTH) return false;
            int bodyLength = Integer.parseInt(new String(bts, 0, HEAD_BODY_LENGTH), 10);
            int allLeng = bodyLength+HEAD_BODY_LENGTH;
            if(allLeng>bts.length) return false;
            byte[] sendBys = new byte[allLeng];
            System.arraycopy(bts, 0, sendBys, 0, allLeng);
            out.write(new BusinessBean(sendBys));
            bufferLeng = bts.length-allLeng;
            if(bufferLeng==0){//缓存数据发送完毕
                bts = null;
                return false;
            }
            byte[] last = new byte[bufferLeng];
            System.arraycopy(bts, allLeng, last, 0, bufferLeng);
            this.bts = last;
            return true;
        }
    }
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值