目录:

  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 基于构造函数中的固定长度
    该类很简单,构造方法中,传入一个整数,该解码器就会按照这个数字对累积区的字节进行切分。

  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!!!!

Netty 粘包 & 拆包 & 编码 & 解码 & 序列化 介绍的更多相关文章

  1. Netty 粘包 拆包 | 史上最全解读

    Netty 粘包/半包原理与拆包实战(史上最全) 疯狂创客圈 Java 聊天程序[ 亿级流量]实战系列之13 [博客园 总入口 ] 本文的源码工程:Netty 粘包/半包原理与拆包实战 源码 本实例是 ...

  2. Netty 粘包/拆包应用案例及解决方案分析

    熟悉TCP变成的可以知道,无论是客户端还是服务端,但我们读取或者发送消息的时候,都需要考虑TCP底层粘包/拆包机制,下面我们先看一下TCP 粘包/拆包和基础知识,然后模拟一个没有考虑TCP粘包/拆包导 ...

  3. 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)

    当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...

  4. java架构之路-(netty专题)netty的编解码(出入战)与粘包拆包

    上次回归: 上次博客我们主要说了netty的基本使用,都是一些固定的模式去写的,我们只需要关注我们的拦截器怎么去写就可以了,然后我们用我们的基础示例,改造了一个简单的聊天室程序,可以看到内部加了一个S ...

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

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

  6. TCP粘包/拆包 ByteBuf和channel 如果没有Netty? 传统的多线程服务器,这个也是Apache处理请求的模式

    通俗地讲,Netty 能做什么? - 知乎 https://www.zhihu.com/question/24322387 谢邀.netty是一套在java NIO的基础上封装的便于用户开发网络应用程 ...

  7. 深入学习Netty(5)——Netty是如何解决TCP粘包/拆包问题的?

    前言 学习Netty避免不了要去了解TCP粘包/拆包问题,熟悉各个编解码器是如何解决TCP粘包/拆包问题的,同时需要知道TCP粘包/拆包问题是怎么产生的. 在此博文前,可以先学习了解前几篇博文: 深入 ...

  8. 【转】Netty之解决TCP粘包拆包(自定义协议)

    1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...

  9. Netty之解决TCP粘包拆包(自定义协议)

    1.什么是粘包/拆包 一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消 ...

随机推荐

  1. python insert所用 插入到自定的位置

    a = list(range(50)) b = list(range(50)) c = [] for x in a: c.insert(x, [a[x], b[x]]) print(c)

  2. JVM运行时数据区(二)

    4.本地方法栈 本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务. 与Java虚拟机栈一样本地 ...

  3. 【设计经验】2、ISE中ChipScope使用教程

    一.软件与硬件平台 软件平台: 操作系统:Windows 8.1 开发套件:ISE14.7 硬件平台: FPGA型号:XC6SLX45-CSG324 二.ChipScope介绍 ChipScope是X ...

  4. 使用git clone命令克隆github项目到本地时出错,提示没有权限的解决方法

    最近使用 git clone 命令在Github上克隆自己项目到本地时出错:提示没有权限,确认仓库是否存在,如下图红色框所示 问题:用过 git 的小伙伴都知道克隆项目的命令是—— git clone ...

  5. Linux的 Shell 理解和使用

    Shell为命令解释器,把用户命令解释为内核可识别的机器语言,然后翻译命令结果给用户,其也是一个强大的编程语言,是解释执行的脚本语言,无编译,与python机制基本相同. Shell内部可以直接调用L ...

  6. 如何在vue中使用ts

    注意:此文并不是把vue改为全部替换为ts,而是可以在原来的项目中植入ts文件,目前只是实践阶段,向ts转化过程中的过渡. ts有什么用? 类型检查.直接编译到原生js.引入新的语法糖 为什么用ts? ...

  7. [P5170] 类欧几里得算法

    "类欧几里得算法"第二题 P5170 [题意]已知\(n,a,b,c\),求 \[ \begin{aligned} f_{1}(a,b,c,n)&=\sum_{i=0}^n ...

  8. 课程五(Sequence Models),第二 周(Natural Language Processing & Word Embeddings) —— 2.Programming assignments:Emojify

    Emojify! Welcome to the second assignment of Week 2. You are going to use word vector representation ...

  9. list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

  10. 在Mac OSX上配置Appium+Android自动化测试环境

    前提准备 开始正文之前,你需要准备好一些基本条件: 1.安装好Mac OSX 操作系统的设备 2.能够访问中国局域网以外资源的方法(没有也行,但很痛苦) 3.已经安装好 homebrew 4.已经安装 ...