在使用mina框架时,很可能会出现粘包、断包和半包的情况,以下针对这些情况进行解决。
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;//处理成功,让父类进行接收下一个包
}
}
}
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框架中数据传输出现粘包、断包和半包问题。