package com.dyz.gameserver.net.codec; 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; import com.dyz.gameserver.commons.message.ClientRequest; import com.dyz.gameserver.net.MinaMsgHandler; /** * 消息解码器。将连续的字节按照协议规范分割成完整的消息包,并包装成ClientRequest。 * @author dyz */ public class GameMsgDecoder extends CumulativeProtocolDecoder { private static final Logger logger = LoggerFactory.getLogger(MinaMsgHandler.class); public GameMsgDecoder() { } /** * flag(1 byte)+length(4 byte,后边内容的长度)+protocol code(4 byte)+content * length的长度包括 :消息号+ 内容 */ @Override protected boolean doDecode(IoSession session, IoBuffer iobuffer, ProtocolDecoderOutput protocolDecoderOutput) throws Exception { if(iobuffer.remaining()<(MsgProtocol.flagSize+MsgProtocol.lengthSize+MsgProtocol.msgCodeSize)){//数据不完整 logger.info("数据包长度不足"); return false; } iobuffer.mark(); byte flag = iobuffer.get();//flag,备用 if (flag == 1) { int length = iobuffer.getInt();//读取长度字段 if(length<=0 || length>MsgProtocol.maxPackLength){//长度字段异常 logger.info("数据包长度异常"); return false; } if(iobuffer.remaining()>=length){// int preLimit = iobuffer.limit();//记录下当前的limit值 /** * 这行代码有一个bug, * 读取协议内容时,如果第一个字节不是1,则越过此字节继续往后的读,直到读到1, * 然而在设置limit时没有考虑到越过去的flag之前的字节,从而导致设置的limit比本应设置的位置小。 * 所以导致,iobuffer中当前position到设置的limit的长度小于我们要读取的length。 * 结果导致抛出BufferUnderflowException */ //iobuffer.limit(MsgProtocol.flagSize+MsgProtocol.lengthSize+length); iobuffer.limit(iobuffer.position()+length); byte[] body = new byte[length]; iobuffer.get(body); iobuffer.limit(preLimit); ClientRequest message = new ClientRequest(body); protocolDecoderOutput.write(message); return true; }else{ logger.info("数据包尚不完整"); iobuffer.reset(); return false; } }else{ logger.info("flag 错误"); return false; } } public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception { } public void dispose(IoSession session) throws Exception { } }