zoukankan      html  css  js  c++  java
  • 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.     }  
  • 相关阅读:
    MISC | ctfshow 31
    010editor 没有分块高亮显示了
    BUUCTF | [网鼎杯 2020 朱雀组]phpweb
    python2与python3共存后,如何使用
    kali2020 装不上docker
    php代码审计整理
    [MRCTF2020]Ezpop
    kali没有tcptraceroute如何安装
    [BUUCTF] 真的很杂
    【弱网测试】备份弱网测试相关数据
  • 原文地址:https://www.cnblogs.com/balaamwe/p/2319190.html
Copyright © 2011-2022 走看看