1.先解释下什么叫粘包和断包

粘包 就是数据以字节的形式在网络中传输,一个数据包的字节可能经过多次的读取粘合才能形成一个完整的数据包

断包 一次读取的内容可能包含了两个或多个数据包的内容,那么我们必须要把当前正在读取的数据包的内容读完整,后面的内容交给其他的数据包去处理

2.粘包和断包是只针对解码(拆包)而言的,编码不需要考虑这件事

3.如果你是个新手,你要明白拆包封包和序列化和反序列化不是一回事,拆包封包是针对协议格式而言的,而序列化和反序列化是针对包体部分编解码而言的

废话说的不少,但是初接触的人来说,这些概念常常是混淆的,mina在粘包断包处理上做的很完善,直接提供了解决方法,但是我们还是要研究下他是怎么做的

org.apache.mina.filter.codec.CumulativeProtocolDecoder   mina中这个类就是在做粘包和断包处理

采用的原理说明:协议格式中可以在协议的头部使用1,2,4字节定义消息体的中长度,消息体的内容没有读够的时候就一直保存在session中做粘合,直到读取完整[粘包过程],

读取完整的时候,如果后面还有没读取的内容要重复放入session交给后续的包去粘合[断包处理]

例如我项目采用的方式

协议说明:

5个字节协议头+协议体.

协议头1-4字节表示协议长度=协议体长度+协议头长度-4(去掉长度占的4字节),采用网络字节序的整数(高位在前,低位在后)

协议头第5字节为标志字节:该字节的最低位为压缩位:0=协议体未压缩 1=协议体已经压缩,该字节的低2-4位为协议位:000=基于AMF3的协议,001=基于java serial协议 , 5-8位未用,作为以后扩展。

1

2

3

4

5

标志位

数据(AMF3或者java serial)

看看这个类CumulativeProtocolDecoder怎么写的, 参考一篇非常好的文章http://www.blogjava.net/landon/archive/2013/12/02/407122.html

CumulativeProtocolDecoder#decode实现

/**
    * 1.缓存decode中的IoBuffer in至session的attribute
    * 2.循环调用doDecode方法直到其返回false
    * 3.解码结束后缓存的buffer->压缩
    */
    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
       // 判断传输层是否存在消息分片,如果不分片则直接doDecode.(可参考TCP/IP详解)
        if (!session.getTransportMetadata().hasFragmentation()) {
            while (in.hasRemaining()) {//hasRemaining()网络中是否还有没读完的内容
                if (!doDecode(session, in, out)) {//这是个抽象方法,调用的是子类中实现的doDecode方法
                    break;
                }
            }

            return;
        }

        boolean usingSessionBuffer = true;
        IoBuffer buf = (IoBuffer) session.getAttribute(BUFFER);//session中的就是你已经读到的所有字节,in中的就是等待读取的内容,这两要粘合在一起
        // 如果session中有BUFFER这个attribute则直接执行追加,否则直接用网络层读到的buffer
        if (buf != null) {
            boolean appended = false;
            // Make sure that the buffer is auto-expanded.
            if (buf.isAutoExpand()) {//这IOBuffer默认可不是自动扩展的,所以要看我们存入session的是否指定为自动扩展,这里当然是了
                try {
                    buf.put(in);//这方法很明了,就是追加
                    appended = true;
                } catch (IllegalStateException e) {
                    // 可能调用了类似slice的方法,会使父缓冲区的自动扩展属性失效(1.可参考AbstractIoBuffer#recapacityAllowed 2.可参考IoBuffer的实现)
                } catch (IndexOutOfBoundsException e) {
                    // 取消了自动扩展属性(可参考IoBuffer实现)
                }
            }

            if (appended) {
    // 追加成功的话,直接flip
                buf.flip();//重置读取状态,准备从头读,position=0,mark=-1,limit=capacity
            } else {
     // 因为用了派生的方法(父子缓冲区)如slice或取消了自动扩展而导致追加失败->重新分配一个Buffer
                buf.flip();
                IoBuffer newBuf = IoBuffer.allocate(buf.remaining() + in.remaining()).setAutoExpand(true);
                newBuf.order(buf.order());
                newBuf.put(buf);
                newBuf.put(in);
                newBuf.flip();
                buf = newBuf;

                // 更新session属性
                session.setAttribute(BUFFER, buf);
            }
        } else {
    // 此else表示session无BUFFER属性,直接赋值
            buf = in;
            usingSessionBuffer = false;
        }

        // 无限循环直到break 1.doDecode返回false 2.doDecode返回true且buf已无数据 3.异常
        for (;;) {
            int oldPos = buf.position();//这时候因为上面的flip,这里肯定是0啊
            boolean decoded = doDecode(session, buf, out);//这是个抽象方法,调用的是子类中实现的doDecode方法
            if (decoded) {//返回true代表已经取到完整的包,也就是读到了>=包头定义的长度的字节
                if (buf.position() == oldPos) {
        //你都已经读取了字节,那这时候的position一定!=0,如果还等于0那就异常了
                    throw new IllegalStateException("doDecode() can't return true when buffer is not consumed.");
                }

                if (!buf.hasRemaining()) {
        //这就是刚刚好的效果,没有没读的数据,正好够完整的包
                    break;
                }
            } else {
                break;
            }
        }

        // 如果经过decode,buffer依然有剩余数据则存储到session->这样下次decode的时候就可以从session取出buffer并执行追加了
        if (buf.hasRemaining()) {
            if (usingSessionBuffer && buf.isAutoExpand()) {
                //后续次就压缩
                buf.compact();
            } else {
                  //首次就直接进session
                storeRemainingInSession(buf, session);
            }
        } else {
            if (usingSessionBuffer) {
                removeSessionBuffer(session);
            }
        }
    }
 
 
 
列出我的解码类

public class MutilDecoder extends CumulativeProtocolDecoder {
private static Logger log = LoggerFactory.getLogger(MutilDecoder.class);

/**
* decoder最大长度
*/
private int maxDecodeLen = 5 * 1024 * 1024;

public void setMaxDecodeLen(int maxDecodeLen) {
  this.maxDecodeLen = maxDecodeLen;
}

@Override
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
try {
  while (in.remaining() > 0) {// /这层循环实际上不需要,CumulativeProtocolDecoder已经处理了
    boolean dataAvai = in.prefixedDataAvailable(4, maxDecodeLen);
    if (dataAvai) {
      //System.out.println("*****"+in.getInt(in.position()));
      // 正常Encoder中写入的包头制定长度数据
      int len = in.getInt();//读4个字节

      byte flag = in.get();// 标志位//读第5个字节

      // 是否压缩
      boolean compressed = ((flag & 0x1) == MutliEncoderNew.BIT_COMPRESSED);

      // //先把需要的字节数读到数组中,防止decode出错后有剩余的字节保留在IoBuffer,使下一个请求解析不了
      byte bytes[] = new byte[len - 1];
      in.get(bytes, 0, len - 1);//读取包体的字节
      if ((flag & 0xE) == MutliEncoderNew.BIT_JAVA) {
        javaDecode(out, bytes, compressed);//java反序列化
      } else {
        amf3Decode(out, bytes, compressed);//amf3反序列化
      }
      //System.out.println("========1");
    } else {
      // 包长度不正确,等待后续包
      if (log.isDebugEnabled()) {
        log.debug("包长度不正确,等待后续包.......");
      }
      //System.out.println(":::总长度"+in.getInt(in.position())+",本次接收长度:"+in.remaining());
      // System.out.println("length is error");
      return false;
    }

  }
} catch (BufferDataException e) {
  log.error("解码数据长度不在限制范围内,丢弃并关闭session.{}", session);
  session.close(true);
  throw e;
} catch (Exception e) {
  log.error(e.getMessage());
  throw e;
}
 return true;
}
}

【MINA】粘包断包处理的更多相关文章

  1. UNIX网络编程——Socket/TCP粘包、多包和少包, 断包

    为什么TCP 会粘包 前几天,调试mina的TCP通信, 第一个协议包解析正常,第二个数据包不完整.为什么会这样吗,我们用mina这样通信框架,还会出现这种问题? TCP(transport cont ...

  2. Socket/TCP粘包、多包和少包, 断包

    转发: https://blog.csdn.net/pi9nc/article/details/17165171 为什么TCP 会粘包 前几天,调试mina的TCP通信, 第一个协议包解析正常,第二个 ...

  3. 为什么TCP 会粘包断包UDP不会

    TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发 ...

  4. Mina框架断包、粘包问题解决方式

    Mina框架断包.粘包问题解决方式 Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然.也能够提供JAVA 对象的序 ...

  5. mina框架tcpt通讯接收数据断包粘包处理

    用mina做基于tcp,udp有通讯有段时间了,一直对编码解码不是很熟悉,这次做项目的时候碰到了断包情况,贴一下解决过程, 我接受数据格式如下图所示: unit32为c++中数据类型,代表4个字节,由 ...

  6. NIO框架之MINA源码解析(四):粘包与断包处理及编码与解码

    1.粘包与段包 粘包:指TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.造成的可能原因: 发送端需要等缓冲区满才发送出去,造成粘包 接收 ...

  7. mina websocket 粘包、断包、(丢包)解决心得

    被这3个(其实是2个)问题坑惨了,目前没发现存在丢包问题,之前认为的丢包问题事实是不存在的. 粘包和断包的情况是存在的,这两个问题不怕,只要发送接收到的数据包顺序没有被打乱颠倒,一切都好办. 容易掉的 ...

  8. TCP 的断包和粘包

    以太网中存在一个对于帧的有效数据大小的限制,即 MTU,以太网的 MTU 为 1500 字节. 一.断包 就是说发送端一次发送的消息长度过大,如果超过了 MTU,那么 ip 会对其进行分片. 在网络编 ...

  9. Netty 粘包/半包原理与拆包实战

    Java NIO 粘包 拆包 (实战) - 史上最全解读 - 疯狂创客圈 - 博客园 https://www.cnblogs.com/crazymakercircle/p/9941658.html 本 ...

随机推荐

  1. office在线预览方案

    一.服务器先转换为PDF,再转换为SWF,最后通过网页加载Flash预览 微软方:利用Office2007以上版本的一个PDF插件SaveAsPDFandXPS.exe可以导出PDF文件,然后再利用免 ...

  2. 用python的numpy作线性拟合、多项式拟合、对数拟合

    转自:http://blog.itpub.net/12199764/viewspace-1743145/ 项目中有涉及趋势预测的工作,整理一下这3种拟合方法:1.线性拟合-使用mathimport m ...

  3. HW7.8

    import java.util.ArrayList; import java.util.Scanner; public class Solution { public static void mai ...

  4. hdoj 5500 Reorder the Books

    Reorder the Books Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  5. placeholder 兼容IE9以下版本 包含pasword

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. HDU 3635 并查集+路径压缩+记录每个点移动次数

    题意: 给定n个点 oper个操作 每个点有1个龙珠 下面2种操作: T u v 把u点所有龙珠搬到v Q u  问u点当前所在城市 u点所在城市有几个龙珠 u点被移动几次 思路: 并查集可以求出 u ...

  7. 剑指OFFER之从1到n中出现1的次数(九度OJ1373)

    题目描述: 亲们!!我们的外国友人YZ这几天总是睡不好,初中奥数里有一个题目一直困扰着他,特此他向JOBDU发来求助信,希望亲们能帮帮他.问题是:求出1~13的整数中1出现的次数,并算出100~130 ...

  8. github快速入门(一)

    一.github介绍 git是一款非常知名的代码托管工具.当然现在有了github for windows版本(类似于 svn tortoise). GitHub for Windows 是一个 Me ...

  9. 对PostgreSQL xmax的理解

    xmax The identity (transaction ID) of the deleting transaction, or zero for an undeleted row version ...

  10. libpq中调用prepared statement:

    代码如下: [root@lex tst]# cat testlibpq.c /* * testlibpq.c * Test the C version of LIBPQ, the POSTGRES f ...