Netty 粘包与拆包

粘包 拆包 原理浅析 Netty中的应用 

Netty 粘包与拆包_第1张图片

2016年拍摄于台湾省日月潭制高点慈恩塔,只有这个地方才能才看什么是日哪个是月。

Netty 粘包与拆包_第2张图片

  微信公众号

Netty 粘包与拆包_第3张图片

王皓的GitHub:https://github.com/TenaciousDWang

 

        今天来说一下网络通信过程中一些不可避免的数据包粘包与半包问题,应该如何解决。

 

        虽然我们在应用层Netty中都是已一个完整的数据包编码发送的,但是到了系统底层及网络层仍然是以字节流发送数据的,数据到了另一端仍然是按照字节流读取的,这个时候会存在两种错误的情况。

 

        一种是我们发出的多个数据包转为字节流后,被另一端读取到的字节流可能为多个数据包粘在一起的字节数据,经过解码后,可能出现多条信息或者错误,另一种是发出的一个数据包的字节流。

        

        另一端由于网络原因只读取到一半,在解码过程中,可能只读取到一半信息或者报错。也就是说通信两端不能保证发出与接收的字节数是对等的,可能存在偏差和延时。

 

        因此我们需要定义一套规则,让客户端按照规则组装数据包,我们称之为粘包,让服务端按照规则来读取数据包,我们称之为拆包,粘包与拆包是相对的,例如,服务端将三个数据包拼成两个TCP包发送出去,服务端则将两个TCP包拆开后重新组装为三个数据包,这个过程就是粘包与拆包的过程。

 

        通常我们无法控制数据在网络中的传输过程,只能在一端读取时想办法,这里我们就来说一下如何来拆包,一般拆包,需要做一个缓冲区域,不断的读取字节数据到缓冲区,然后每次都要从缓冲区中读取数据包,直到读取出一个完整的数据包,不完整的片段还是要继续放在缓冲区,等到完整后再读取。如果我们手动拆包,会很麻烦,在这里Netty为我们提供了开箱即用的拆包神器,接下来我们来看一下。

 

1. 固定长度的拆包器 FixedLengthFrameDecoder

Netty 粘包与拆包_第4张图片

2. 行拆包器 LineBasedFrameDecoder

Netty 粘包与拆包_第5张图片

3. 分隔符拆包器 DelimiterBasedFrameDecoder

Netty 粘包与拆包_第6张图片

4. 基于长度域拆包器 LengthFieldBasedFrameDecoder

Netty 粘包与拆包_第7张图片

        基于长度域拆包器最通用的一种拆包器,这里我们来说一下,其他的拆包器官方文档中已经列举了很多例子这里不再描述。

 

    new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 74);

 

       LengthFieldBasedFrameDecoder类构造函数有多个重载,这里我们只根据当前通信协议说一种最常用的,第一个参数为数据包最大长度,第二个参数为数据长度域的偏移量,也就是数据长度所在数据包内的开始位置,第三个参数为数据长度域的长度,也就是我们用来描述数据长度的空间为4个字节。

 

      

        根据我们当前的通信协议。魔数4个字节,版本号1个字节,序列化算法标识1个字节,指令1个字节,那么我们可以算出长度域偏移量为4+1+1+1=7。长度域长度为4,最大数据包长度我们用Integer.MAX_VALUE来表示,最大为2GB。

 

Netty 粘包与拆包_第8张图片

 

        之前我们提到过一个魔数的概念,我们可以在拆包时,同时判断该数据包是否是我们支持的协议,如果不是我们可以直接关闭连接,以节省资源。

 

Netty 粘包与拆包_第9张图片

 

  我们创建一个逻辑处理器Spliter,继承LengthFieldBasedFrameDecoder然后在构造器中创建父类,这样就可以获得一个完整的数据包,然后覆写父类decode方法,这个方法每次都会传入一个完整的ByteBuf,我们读取头4个字节获得魔数,如果不是支持的协议,直接关闭连接,如果支持直接返回调用decode的结果,即一个完整的ByteBuf数据包。

 

 

        到此我们的客户端与服务端的pipeline结构为下图。

 

Netty 粘包与拆包_第10张图片

 

 

 

你可能感兴趣的:(Netty,IM实战,java,netty,io,高性能)