ApacheMINA粘包、拆包的正确实现(网上常见的代码有bug)

2016-07-21· 5564 次浏览
粘包拆包的关键在于数据协议,通俗的讲就是我们要约定数据包的格式, 这个实例中的格式是(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;// 处理成功,让父类进行接收下个包     } } ```