ApacheMINA粘包、拆包的正确实现(网上常见的代码有bug)
粘包拆包的关键在于数据协议,通俗的讲就是我们要约定数据包的格式,
这个实例中的格式是(4个字节长度+json字符串)
1.
```
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;
/**
* @author Mr.Xia
*/
public class MessageCodecFactory implements ProtocolCodecFactory {
private final DataEncoderEx encoder;
private final DataDecoderEx decoder;
public MessageCodecFactory() {
encoder = new DataEncoderEx();
decoder = new DataDecoderEx();
}
@Override
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
return decoder;
}
@Override
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return encoder;
}
}
```
2.
```
import java.nio.charset.Charset;
import org.apache.mina.common.IoBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
/**
* @author Mr.Xia
*/
public class DataEncoderEx extends ProtocolEncoderAdapter {
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
System.out.println(message);
IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);
String strOut = message.toString();
buf.putInt(strOut.getBytes(Charset.forName("utf-8")).length);
buf.putString(strOut, Charset.forName("utf-8").newEncoder());
buf.flip();
out.write(buf);
}
}
```
3.
```
import org.apache.mina.common.IoBuffer;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
/**
* @author Mr.Xia
*/
public class DataDecoderEx extends CumulativeProtocolDecoder {
@Override
protected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {
if (in.remaining() < 4)// 这里很关键,网上很多代码都没有这句,是用来当拆包时候剩余长度小于4的时候的保护,不加会出错
{
return false;
}
if (in.remaining() > 1) {
in.mark();// 标记当前位置,以便reset
int length = in.getInt(in.position());
if (length > in.remaining() - 4) {// 如果消息内容不够,则重置,相当于不读取size
System.out.println("package notenough left=" + in.remaining()+ " length=" + length);
in.reset();
return false;// 接收新数据,以拼凑成完整数据
} else {
System.out.println("package =" + in.toString());
in.getInt();
byte[] bytes = new byte[length];
in.get(bytes, 0, length);
String str = new String(bytes, "UTF-8");
if (null != str && str.length() > 0) {
String strOut = str;// 别看这里的处理,这里是我的数据包解密算法~你可以直接拿str当数据
out.write(strOut);
}
if (in.remaining() > 0) {// 如果读取内容后还粘了包,就让父类再给一次,进行下一次解析
// System.out.println("package left="+in.remaining()+" data="+in.toString());
}
return true;// 这里有两种情况1:没数据了,那么就结束当前调用,有数据就再次调用
}
}
return false;// 处理成功,让父类进行接收下个包
}
}
```