Mina 粘包、断包、半包解决

在使用mina框架时,很可能会出现粘包、断包和半包的情况,以下针对这些情况进行解决。

一、CumulativeProtocolDecoder详解
A. 你的doDecode()方法返回true 时,CumulativeProtocolDecoder 的decode()方法会首先判断你是否在doDecode()方法中从内部的IoBuffer 缓冲区读取了数据,如果没有,则会抛出非法的状态异常,也就是你的doDecode()方法返回true 就表示你已经消费了本次数据(相当于聊天室中一个完整的消息已经读取完毕),进一步说,也就是此时你必须已经消费过内部的IoBuffer 缓冲区的数据(哪怕是消费了一个字节的数据)。如果验证过通过,那么CumulativeProtocolDecoder 会检查缓冲区内是否还有数据未读取,如果有就继续调用doDecode()方法,没有就停止对doDecode()方法的调用,直到有新的数据被缓冲。

B. 当你的doDecode()方法返回false 时,CumulativeProtocolDecoder 会停止对doDecode()方法的调用,但此时如果本次数据还有未读取完的,就将含有剩余数据的IoBuffer 缓冲区保存到IoSession 中,以便下一次数据到来时可以从IoSession 中提取合并。如果发现本次数据全都读取完毕,则清空IoBuffer 缓冲区。简而言之,当你认为读取到的数据已经够解码了,那么就返回true,否则就返回false。这个CumulativeProtocolDecoder 其实最重要的工作就是帮你完成了数据的累积,因为这个工作是很烦琐的。也就是说返回true,那么CumulativeProtocolDecoder会再次调用decoder,并把剩余的数据发下来;返回false就不处理剩余的,当有新数据包来的时候就把剩余的数据和新的数据拼接在一起,然后再调用decoder。

二、实例

1.实现ProtocolCodecFactory类,用来拦截数据包。

package com.avcon.plugin.mina;

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 MinaMessageFactory implements ProtocolCodecFactory {
	
	private MessageDecoder decoder;
	private MessageEncoder encoder;
	
	

	public MinaMessageFactory() {
		this.decoder = new MessageDecoder();
		this.encoder = new MessageEncoder();
	}

	@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;
	}

}
2.再在在chain里面注册编解码器。
//connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaMessageFactory()));
chain.addLast("codec", new ProtocolCodecFilter(new MessageCodecFactory()));
PS:注意放在多线程前,否则会导致解码混乱的情况。

3.实现decode和encoder
继承重写CumulativeProtocolDecoder类:

package com.avcon.plugin.mina;

import java.io.IOException;
import net.sf.json.JSONObject;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
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;


public class MessageDecoder extends CumulativeProtocolDecoder {

	@Override
	protected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {
		if(in.remaining() < 4){//正常当长度小于4的时候说明断包了
			return false;
		}else{
			in.mark();//标记当前位置
			//这个方法会导致potion的改变,加4,作用是获取前面从指定位置开始的4个字节
			int len = in.getInt(in.position());
			if(len > in.remaining()){
				in.reset();//消息不够,断包处理
				return false;
			}else{
				byte[] bytes = new byte[len];
				in.get(bytes,0,len);
				//返回数据包消息,可以在这里对返回数据包消息进行处理
				System.out.Println("消息:" + new String(bytes,"utf-8"));
				//只有调用了这个方法,IoHandlerAdapter中的messageReceived方法才会被调用
		       out.write("Message to succeed");
			}
			if(in.remaining() > 0){//如果还粘了包,就让父类在进行一次处理
				return true;
			}
			return false;//处理成功,让父类进行接收下一个包
		}
	}

}

或通过下面这种方式将完整数据包交给IoHandlerAdapter类中的messageReceived方法来处理。
package com.avcon.plugin.mina;

import java.io.IOException;
import net.sf.json.JSONObject;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
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;


public class MessageDecoder extends CumulativeProtocolDecoder {

	@Override
	protected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {
		if(in.remaining() < 4){//正常当长度小于4的时候说明断包了
			return false;
		}else{
			in.mark();//标记当前位置
			byte[]	bs = new byte[4];
			in.get(bs);
			int len = ByteHandler.byteToInt(bs);//调用方法将byte数组转换为int
			if(len > in.remaining()){
				in.reset();//消息不够,断包处理
				return false;
			}else{
				byte[] bytes = new byte[len];
				in.get(bytes,0,len);
				//创建一个包含一个完整数据包的IoBuffer对象
				IoBuffer buffer = IoBuffer.wrap(bs);
				buffer.put(bytes);
				//将IoBuffer对象写出,在IoHandlerAdapter类的messageReceived方法中进行处理
		        out.write(buffer);
			}
			if(in.remaining() > 0){//如果还粘了包,就让父类在进行一次处理
				return true;
			}
			return false;//处理成功,让父类进行接收下一个包
		}
	}


继承重写ProtocolEncoderAdapter类:

package com.avcon.plugin.mina;

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 com.avcon.plugin.handler.ByteHandler;

public class MessageEncoder extends ProtocolEncoderAdapter {

	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out)
			throws Exception {
		IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);
		//这个判断可根据自己的实际情况而定
		if("String".equals(message.getClass().getSimpleName())){
			String msg = (String) message;
			byte[] bytes = msg.getBytes();
			byte[] heads = ByteHandler.intToBytes(bytes.length);//调用方法将int数转为byte数组
			buf.put(heads);
			buf.put(bytes);
		}else if("byte[]".equals(message.getClass().getSimpleName())){
			byte[] bytes = (byte[]) message;
			buf.put(bytes);
		}
		buf.flip();//不可缺少
		out.write(buf);
	}
	
}
通过以上代码,可以解决mina框架中数据传输出现粘包、断包和半包问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值