使用過mina的同學(xué)應(yīng)該都遇到到過,在解碼時少包、多包的問題,查閱了很多資料還是迷迷糊糊的,經(jīng)過 不懈努力,終于解決了。原來解決方法是那樣的簡單。廢話少說,請看列子。
問題:我發(fā)送的是xml字符串?dāng)?shù)據(jù),在發(fā)送數(shù)據(jù)后,接收方在解碼的時候可能接到1條,也可能是多條,還 可能是半條或一條半,解決方法就是使用CumulativeProtocolDecoder
首先,在編碼的時候要把前4位設(shè)成標(biāo)志位,標(biāo)志消息內(nèi)容的長度。里面的重點是doDecode的返回值,一 定要繼承CumulativeProtocolDecoder 哦。
請看decode的寫法:
public class AsResponseDecoder extends CumulativeProtocolDecoder {
private static Logger LOG = LoggerFactory.getLogger(AsResponseDecoder.class);
private final Charset charset;
public AsResponseDecoder(Charset charset){
this.charset = charset;
}
/**
* 這個方法的返回值是重點:
* 1、當(dāng)內(nèi)容剛好時,返回false,告知父類接收下一批內(nèi)容
* 2、內(nèi)容不夠時需要下一批發(fā)過來的內(nèi)容,此時返回false,這樣父類
CumulativeProtocolDecoder
* 會將內(nèi)容放進IoSession中,等下次來數(shù)據(jù)后就自動拼裝再交給本類的doDecode
* 3、當(dāng)內(nèi)容多時,返回true,因為需要再將本批數(shù)據(jù)進行讀取,父類會將剩余的數(shù)據(jù)再次推送本
類的doDecode
*/
public boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
CharsetDecoder cd = charset.newDecoder();
if(in.remaining() > 0){//有數(shù)據(jù)時,讀取4字節(jié)判斷消息長度
byte [] sizeBytes = new byte[4];
in.mark();//標(biāo)記當(dāng)前位置,以便reset
in.get(sizeBytes);//讀取前4字節(jié)
//NumberUtil是自己寫的一個int轉(zhuǎn)byte[]的一個工具類
int size = NumberUtil.byteArrayToInt(sizeBytes);
//如果消息內(nèi)容的長度不夠則直接返回true
if(size > in.remaining()){//如果消息內(nèi)容不夠,則重置,相當(dāng)于不讀取size
in.reset();
return false;//接收新數(shù)據(jù),以拼湊成完整數(shù)據(jù)
} 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){//如果讀取內(nèi)容后還粘了包,就讓父類再給俺
一次,進行下一次解析
return true;
}
}
}
return false;//處理成功,讓父類進行接收下個包
}
}
下面附上Encode類
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;
String xml = AsXmlPacker.pack(respCmd);//將對象轉(zhuǎn)成xml
byte[] bytes = xml.getBytes();
byte[] sizeBytes = NumberUtil.intToByteArray(bytes.length);
buffer.put(sizeBytes);//將前4位設(shè)置成數(shù)據(jù)體的字節(jié)長度
buffer.put(bytes);//消息內(nèi)容
buffer.flip();
out.write(buffer);
}
}
這樣的話,如果數(shù)據(jù)不完整(斷包了)那不就是返回false,繼續(xù)接收下個包的數(shù)據(jù),然后拼裝起來,再次進行doDecode解析,如果還不夠就會繼續(xù)接收,直到出現(xiàn)一個完整的包(非size > in.remaining()),就會執(zhí)行第一個else中的代碼,封裝對象,然后write出去。在這之后再判斷是不是有多余的數(shù)據(jù)(粘包了),如果有,返回true再次繼續(xù)doDecode方法進行對buffer的解析。只要你每次解析的數(shù)據(jù)是按照你所定義的,就不會出現(xiàn)問題。如果出1個字節(jié)的錯誤,那可就全錯了。