Netty 粘包 & 拆包 & 编码 & 解码 & 序列化 介绍

目录:设计模式

  1. 粘包 & 拆包及解决方案 ByteToMessageDecoder
  2. 基于长度编解码器
  3. 基于分割符的编解码器
  4. google 的 Protobuf 序列化介绍
  5. 其余的

 前言

Netty 做为一个网络框架,对 TCP 链接中的问题都作了全面的考虑,好比粘包拆包致使的半包问题,如何编解码,如何实现私有协议,序列化等等。本文主要针对这些问题作一个简单介绍,目的是想对整个 Netty 的编解码框架作一个全盘的审视,以确保在后面的源码学习中不会一叶障目不见泰山。缓存

1. 粘包 & 拆包及解决方案 ByteToMessageDecoder

因为TCP是面向字节流的,什么意思呢:虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅当作式一连串的无结构的字节流。TCP 并不知道所传送的字节流的含义。网络

所以 TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具备对应大小的关系(例如,发送方应用程序交给发送方的 TCP 共 10 个数据块,但接收方的 TCP 可能只用了 4 个就把收到的字节流交付上层的应用程序)。框架

同时,TCP 不关心应用进程一次把多长的报文发送到 TCP 的 缓存 中,而是根据对方给出的窗口值和当前网络阻塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。若是应用进程传送到 TCP 缓存的数据块太长,TCP 就能够把他划分短一点再传送。若是应用程序一次只发来一个字节,TCP 也能够等待积累有足够多的字节后再构成报文段发送出去。异步

  • TCP 发送报文通常是 3 个时机:
  1. 缓冲区数据达到 最大报文长度 MSS
  2. 由发送端的应用进程指明要求发送报文段,即 TCP 支持的推送(push)操做;
  3. 当发送方的一个计时器期限到了,即便长度不超过 MSS ,也发送。

以上引自《计算机网络-----谢希仁》。函数

说了这么多,TCP 的这种机制,会致使什么问题呢?粘包问题。有了粘包,就须要拆包。工具

  • 通常解决粘包拆包问题有 4 种办法:
  1. 固定数据的长度,好比 100 字节,若是不够就补空格。
  2. 学习 HTTP ,FTP 等,使用回车换行符号。
  3. 将消息分为 head 和 body,head 中包含 body 长度的字段,通常 head 的第一个字段使用 int 值来表示 body 长度。
  4. 使用更复杂的应用层协议(等于没说 =_= !)。

Netty 做为一个网络框架,直接和 TCP 打交道,天然考虑了这个问题。而解决这个问题的主要实现就是抽象类 ByteToMessageDecoder,详见 Netty 解码器抽象父类 ByteToMessageDecoder 源码解析。Netty 使用了模板设计模式,这个类只定义了共有行为,具体解码实现仍是子类,好比上面提到的 4 种方式。性能

2. 基于长度编解码器的具体实现

基于长度的实现有2个现成的类:学习

  1. FixedLengthFrameDecoder 基于构造函数中的固定长度
    该类很简单,构造方法中,传入一个整数,该解码器就会按照这个数字对累积区的字节进行切分。google

  2. LengthFieldBasedFrameDecoder 基于流中动态的长度
    该类比较复杂。构造函数参数多达 6 个,在构建私有协议栈时大有用处。

3. 基于分割符的编解码器

一样有 2 个:

  1. DelimiterBasedFrameDecoder 用户提供分割符。
    该类比较简单,根据用户提供的分割符对累积区的内容进行分割。性能相对不是那么完美。

  2. LineBasedFrameDecoder 基于换行符,支持多种换行符 \n \r\n 速度相比自定义较快。
    该类使用更简单,根据换行符进行拆包粘包。

4. google 的 ProtobufDecoder ProtobufEncoder 序列化介绍

Netty 中有不少序列化工具,好比 Jboss 的 Marshalling,同时也支持 Java 标准的序列化。 但咱们重点关注 google 的 protobuf 库。由于它的性能最高。

上面的 4 个解码器都是基于 ByteToMessageDecoder,将粘包的字节转为用户须要的字节。而ProtobufDecoder 不是继承自 ByteToMessageDecoder,而是继承自 MessageToMessageDecoder,名字都不一样。MessageToMessageDecoder 的做用是什么呢?

从名字上看,该类用于将两个消息进行转换(好比一种 POJO 转成另外一种)。后面咱们将花大篇幅讲述这个类库。

5. 其余的

1. TooLongFrameException

因为 Netty 是一个异步框架,因此须要在字节能够解码以前在内存中缓冲他们。所以不能让解码器缓冲大量的数据以致于耗尽可用的内存。为了解决这个问题,Netty 提供了 TooLongFrameException 类,其将由解码器在帧超出指定的大小限制时抛出异常。

你能够设置一个最大的阈值,当超过该阈值,这抛出异常。

2. 写大型数据的 FileRegion

有时候你可能须要写一个大型的数据,若是不停的写入,可能致使 OOM,因此在写大型数据时,须要准备好处理到远程节点的链接时慢速链接的状况,这种状况会致使内存释放的延迟。

咱们可使用 NIO 的零拷贝特性,这种特性消除了将文件内容从文件系统移动到网络栈的复制过程。而咱们所须要作的就是使用一个 FileRegion 接口的实现。
官方定义:

经过支持零拷贝的文件传输的 Channel 来发送的文件区域。

6. 总结

本文并无刨析源码,主要是针对 Netty 中现有的或者设计的编解码,序列化等工具作一个介绍,方便后面有条不紊的按照这个路线研究他们的具体实现。

good luck!!!!

相关文章
相关标签/搜索