转载自:http://freemart.iteye.com/blog/836654
使用过 mina 的同学应该都遇到到过,在解码时少包、多包的问题,查阅了很多资料还是迷迷糊糊的,经过不懈努力,终于解决了。
原来解决方法是那样的简单。废话少说,请看例子。
另外建了一个交流群:19702042,大家可以在线交流
问题:
我发送的是 xml 字符串数据,在发送数据后,接收方在解码的时候可能接到1条,
也可能是多条,还可能是半条或一条半,解决方法就是使用 CumulativeProtocolDecoder
首先,在编码的时候要把前 4 位设成标志位,标志消息内容的长度。
里面的重点是 doDecode 的返回值,一定要继承 CumulativeProtocolDecoder 哦。
请看 decode 的写法:
package org.bruce.mina.cpp.codec; import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder; 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; public class AsResponseDecoder extends CumulativeProtocolDecoder {
private static Logger logger = LoggerFactory.getLogger(AsResponseDecoder.class);
private final Charset charset; public AsResponseDecoder(Charset charset) {
this.charset = charset;
} /**
* 这个方法的返回值是重点:
* 1、当内容刚好时,返回 false,告知父类接收下一批内容
*
* 2、内容不够时需要下一批发过来的内容,此时返回 false,
* 这样父类 CumulativeProtocolDecoder 会将内容放进 IoSession 中,
* 等下次来数据后就自动拼装再交给本类的 doDecode
*
* 3、当内容多时,返回 true,因为需要再将本批数据进行读取,
* 父类会将剩余的数据再次推送本类的doDecode
*/
public boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception { CharsetDecoder cd = charset.newDecoder();
if (in.remaining() > 0) {
// 有数据时,读取 4 字节判断消息长度
byte [] sizeBytes = new byte[4]; // 标记当前位置,以便 reset
in.mark(); //读取前 4 字节
in.get(sizeBytes); // NumberUtil 是自己写的一个 int 转 byte[] 的一个工具类
int size = NumberUtil.byteArrayToInt(sizeBytes); if (size > in.remaining()) {
// 如果消息内容的长度不够,则重置(相当于不读取 size),返回 false
in.reset();
// 接收新数据,以拼凑成完整数据
return false; } else {
byte[] bytes = new byte[size];
in.get(bytes, 0, size);
String xmlStr = new String(bytes,"UTF-8");
System.out.println("------------" + xmlStr);
if (null != xmlStr && xmlStr.length() > 0) {
AsResponse resCmd = new AsResponse();
AsXmlPacker.parse(resCmd, xmlStr);
if (resCmd != null) {
out.write(resCmd);
}
}
if (in.remaining() > 0) {
// 如果读取内容后还粘了包,就让父类把剩下的数据再给解析一次
return true;
}
}
}
// 处理成功,让父类进行接收下个包
return false;
} }
下面附上 encode 类
package org.bruce.mina.cpp.codec; import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder; 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; public class AsResponseEncoder extends ProtocolEncoderAdapter {
private final Charset charset; public AsResponseEncoder(Charset charset) {
this.charset = charset;
} public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
CharsetEncoder ce = charset.newEncoder();
IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true); AsResponse respCmd = (AsResponse) message; // 将对象转成 xml
String xml = AsXmlPacker.pack(respCmd);
byte[] bytes = xml.getBytes();
byte[] sizeBytes = NumberUtil.intToByteArray(bytes.length); // 将前 4 位设置成数据体的字节长度
buffer.put(sizeBytes);
// 消息内容
buffer.put(bytes);
buffer.flip(); out.write(buffer);
} }