MINA2收包中对粘包的处理

http://scholers.iteye.com/blog/784336

  MINA2中(MINA2 RC版本,MINA2.0正式版已经发布)服务端接受数据默认有一定长度的缓冲区(可以在启动的时候设置)。那么对于大报文,怎么处理呢?比如说超过1024,甚至更多?MINA2为了节省网络流量,提高处理效率,会将大报文自动拆分(可能是存放MINA2中的缓冲区里面):比如2048字节的报文,就会拆分成两次;那么在接受的时候,就有一个如何判断是完整报文的问题,或者说是一个拆包组包的问题。 
  MINA2中初始化服务的时候是可以设置输入和输出的缓冲区的: 
 

Java代码  收藏代码
  1. acceptor.getSessionConfig().setReadBufferSize(1024);  


    MINA2提供的案例是,在IoSession中设置一个类似于session,存在在当前IoSession中的全局变量,在此IoSession中有效。 
 

Java代码  收藏代码
  1. private final AttributeKey TEST = new AttributeKey(getClass(), "TEST");  
  2.     


  大家都知道,通过 SOCKET TCP/IP传输过来的报文是不知道边界的,所以一般会约定在前端固定长度的字节加上报文长度,让SERVER来根据这个长度来确定整个报文的边界,在我前面的博文有提到。其实MINA2中有: 
  prefixedDataAvailable(4) int 
方法,来判断固定长度的报文长度,但是参数只能是1,2,4;该方法很好用。判断前四字节的整型值是否大于等于整个缓冲区的数据。可以方便的判断一次 messageReceived 过来的数据是否完整。(前提是自己设计的网络通讯协议前四字节等于发送数据的长度) ,如果你不是设定1,2,4字节来作为长度的话,那么就没辙了。 
   在你的解码操作中,MINA2的缓冲区发多少次报文,你的decode方法就会调用多少次。 
  上面设置了session之后,可以采用一个方法: 
 

Java代码  收藏代码
  1. /** 
  2.  *  
  3.  * @param session 
  4.  *            会话信息 
  5.  * @return 返回session中的累积 
  6.  */  
  7. private Context getContext(IoSession session) {  
  8.     Context ctx = (Context) session.getAttribute(CONTEXT);  
  9.     if (ctx == null) {  
  10.         ctx = new Context();  
  11.         session.setAttribute(CONTEXT, ctx);  
  12.     }  
  13.     return ctx;  
  14. }  


然后在你的decode方法中,首先从session取出数据对象,进行拼接: 
 

Java代码  收藏代码
  1. Context ctx = getContext(session);  
  2.   
  3.  // 先把当前buffer中的数据追加到Context的buffer当中  
  4.  ctx.append(ioBuffer);  
  5.  // 把position指向0位置,把limit指向原来的position位置  
  6.  IoBuffer buf = ctx.getBuffer();  
  7.  buf.flip();  


  
接着读取每次报文的总长度: 

Java代码  收藏代码
  1. // 读取消息头部分  
  2. byte[] bLeng = new byte[packHeadLength];  
  3. buf.get(bLeng);  
  4. int length = -1;  
  5. try {  
  6.   length = Integer.parseInt(new String(bLeng));  
  7. catch (NumberFormatException ex) {  
  8.   ex.printStackTrace();  
  9. }  
  10. if (length > 0) {  
  11.   ctx.setMsgLength(length);  
  12. }  



在读取到每次报文的长度之后,就接着循环判断BUF里面的字节数据是否已经全部接受完毕了,如果没有接受完毕,那么就不处理;下面是完整处理的代码: 
 

Java代码  收藏代码
  1. while (buf.remaining() >= packHeadLength) {  
  2.     buf.mark();  
  3.     // 设置总长度  
  4.     if (ctx.getMsgLength() <= 0) {  
  5.         // 读取消息头部分  
  6.         byte[] bLeng = new byte[packHeadLength];  
  7.         buf.get(bLeng);  
  8.         int length = -1;  
  9.         try {  
  10.             length = Integer.parseInt(new String(bLeng));  
  11.             } catch (NumberFormatException ex) {  
  12.                         ex.printStackTrace();  
  13.             }  
  14.             if (length > 0) {  
  15.               ctx.setMsgLength(length);  
  16.                }  
  17.             }  
  18.   
  19.   
  20.             // 读取消息头部分  
  21.             int length = ctx.getMsgLength();  
  22.             // 检查读取的包头是否正常,不正常的话清空buffer  
  23.             if (length < 0) { // || length > maxPackLength2) {  
  24.             buf.clear();  
  25.             out.write("ERROR!");  
  26.             break;  
  27.             // 读取正常的消息包,并写入输出流中,以便IoHandler进行处理  
  28.           } else if (length > packHeadLength && buf.remaining() >= length) {  
  29.             //完整的数据读取之后,就可以开始做你自己想做的操作了                       
  30.     } else {  
  31.         // 如果消息包不完整  
  32.         // 将指针重新移动消息头的起始位置  
  33.         buf.reset();  
  34.         break;  
  35.     }  
  36.      }  
  37.     if (buf.hasRemaining()) { // 如果有剩余的数据,则放入Session中  
  38.        // 将数据移到buffer的最前面  
  39.              IoBuffer temp = IoBuffer.allocate(2048).setAutoExpand(  
  40.                         true);  
  41.        temp.put(buf);  
  42.        temp.flip();  
  43.        buf.clear();  
  44.        buf.put(temp);  
  45.   
  46.     } else { // 如果数据已经处理完毕,进行清空  
  47.     buf.clear();  
  48. }  
  49.    


为了便于操作,最好设置一个内部类: 

Java代码  收藏代码
  1. private class Context {  
  2.         private final CharsetDecoder decoder;  
  3.         private IoBuffer buf;  
  4.         private int msgLength = 0;  
  5.         private int overflowPosition = 0;  
  6.   
  7.         /** 
  8.          *  
  9.          *  
  10.          */  
  11.         private Context() {  
  12.             decoder = charset.newDecoder();  
  13.             buf = IoBuffer.allocate(80).setAutoExpand(true);  
  14.         }  
  15.   
  16.         /** 
  17.          *  
  18.          *  
  19.          * @return CharsetDecoder 
  20.          */  
  21.         public CharsetDecoder getDecoder() {  
  22.             return decoder;  
  23.         }  
  24.   
  25.         /** 
  26.          *  
  27.          *  
  28.          * @return IoBuffer 
  29.          */  
  30.         public IoBuffer getBuffer() {  
  31.             return buf;  
  32.         }  
  33.   
  34.         /** 
  35.          *  
  36.          *  
  37.          * @return overflowPosition 
  38.          */  
  39.         public int getOverflowPosition() {  
  40.             return overflowPosition;  
  41.         }  
  42.   
  43.         /** 
  44.          *  
  45.          * 
  46.          * @return matchCount 
  47.          */  
  48.         public int getMsgLength() {  
  49.             return msgLength;  
  50.         }  
  51.   
  52.         /** 
  53.          *  
  54.          *  
  55.          * @param matchCount 
  56.          *            报文长度 
  57.          */  
  58.         public void setMsgLength(int msgLength) {  
  59.             this.msgLength = msgLength;  
  60.         }  
  61.   
  62.         /** 
  63.          *  
  64.          *  
  65.          */  
  66.         public void reset() {  
  67.             this.buf.clear();  
  68.             this.overflowPosition = 0;  
  69.             this.msgLength = 0;  
  70.             this.decoder.reset();  
  71.         }  
  72.   
  73.         /** 
  74.          *  
  75.          * @param in 
  76.          *            输入流 
  77.          */  
  78.         public void append(IoBuffer in) {  
  79.             getBuffer().put(in);  
  80.   
  81.         }  
  82.   
  83.     }  
posted @ 2012-01-11 13:53  balaamwe  阅读(1037)  评论(0编辑  收藏  举报