何为粘包 / 半包?

比如,我们发送两条消息:ABC 和 DEF,那么对方收到的就一定是 ABC 和 DEF 吗?

不一定,对方可能一次就把两条消息接收完了,即 ABCDEF;也可能分成了好多次,比如 AB、CD 和 EF。

对方一次性接收了多条消息这种现象,我们就称之为粘包现象

对方多次接收了不完整消息这种现象,我们就称之为半包现象

为什么会出现粘包 / 半包?

粘包的原因

我们知道,TCP 发送消息的时候是有缓冲区的,当消息的内容远小于缓冲区的时候,这条消息不会立马发送出去,而是跟其它的消息合并之后再发送出去,这样合并发送是明显能够提高效率的。

这就好比寄快递的时候一样,不可能你寄一个快递就给你派送一样,而是先放到仓库里,等够一车了再统一装车拉走。

但是,对方接收到的消息就是一个粘包,无法有效区分出来到底是几条消息。

当然,接收消息也是会通过 TCP 的缓冲区的,如果接收方读取得不及时,也有可能出现粘包现象

比如,缓冲区里面的 ABC 还没来得及读取,又来了一条消息 DEF,这时候两条消息就合并在一起了,也就出现了粘包了。

总结起来,出现粘包的原因无非两种:

  • 发送方发送的消息 < 缓冲区大小
  • 接收方读取消息不及时

半包的原因

类比粘包,如果发送的消息太大,已经超过了缓冲区的大小,这时候就必须拆分发送,也就形成了半包现象

这就好比你发的快递太多了,一车拉不完,要分成好几车拉走一样(似乎不太常见☺)。

还有一种情况,网络协议各层是有最大负载的,所以,对应到各种协议它们是有最大发送数据的限制的,这种可以发送的最大数据称作 MTU(Maximum Transmission Unit,最大传输单元)。

所以一次发送的数据如果超过了协议的 MTU,也要进行拆分发送,也会形成半包现象

总结起来,出现半包的原因有两种:

  • 发送方发送的消息 > 缓冲区的大小
  • 发送方发送的消息 > 协议的 MTU

本质原因

发送消息的时候如果消息太小,先放到缓冲区里面放着,等数据够多了再一起发送;如果消息太大,则拆成多个消息分批发送。

那么,本质原因是缓冲区吗?

当然不是,缓冲区的存在是为了提高发送消息的效率。

本质原因是:TCP 是流式协议,消息无边界。

TCP 协议本身像水流一样,源源不断,完全不知道消息的边界在哪里。

UDP 协议不会出现粘包 / 半包现象,它的消息是有明确边界的,不会像 TCP 一样出现粘包 / 半包现象。

怎么解决粘包 / 半包?

上面我们分析了出现粘包 / 半包现象的本质原因,所以我们只要能解决消息的边界是不是就可以解决粘包 / 半包问题了呢?

答案是肯定的。

所以,业界就衍生出了三种常用的解决粘包 / 半包问题的方法:定长法、分割符法、长度 + 内容法

定长法

固定长度确定消息的边界,比如传输的消息分别为 ABC、D、EF。

那么,就找最长的那条消息,这里是 ABC,那就以 3 为固定长度,不足三位的补足三位。

所以,这种方式最大的缺点就是浪费空间,所以不推荐。

分割符法

使用固定的分割符分割消息,比如传输的消息分别为 ABC、DEFG、HI\n,假如使用 \n 作为分割符。

那么,就在消息的边界处加一个 \n 作为分割符,这样接收方拿到消息之后使用 \n 去分割消息即可。

但是,这种方式的缺点是一是分割符本身作为传输内容时要转义,二是要扫描消息的内容才能确定消息的边界在哪里,所以也不是特别推荐。

长度 + 内容法

使用固定的字节数存储消息的长度,后面跟上消息的内容,读取消息的时候先读取长度,再一次性把消息的内容读取出来。

比如,传输的消息分别为 ABC、DEFG、HI。

那么,就在消息前面分别加上长度一起传输,后面再跟上内容,这样即使三条消息一起传输也可以分得清清楚楚。

这种方式的缺点是需要预先知道消息的最大长度,但是跟这个缺点相比,优点太明显了,所以是我们推荐的方式。

其它方法

上面介绍了业界公认的三种处理粘包 / 半包的方法,那么,还有没有其它方法呢?

其实,可以说有,也可以说没有,万变不离其宗,这三种方法是基础。比如,使用 JSON 协议,可以通过查找匹配到的 {} 的数量,来处理粘包 / 半包,说白了,也是一种分割符法,只不过不是基础版。

比较

下面是三种方法的整体比较:

方法 如何确定消息边界 优点 缺点 推荐度
定长法 使用固定长度分割消息 简单 空间浪费 不推荐
分割符法 使用固定分割符分割消息 简单 分割符本身需要转义,且需要扫描消息的内容 不特别推荐
长度 + 内容法 先获取消息的长度,再按长度读取内容 精确获取消息的内容 需要预先知道消息的最大长度 推荐

Netty 是如何支持的?

Netty 是通过三组类来处理粘包 / 半包问题的,分别对应于上面提到的三种方式。

方法 编码 解码
定长法 FixedLengthFrameDecoder
分割符法 DelimiterBasedFrameDecoder
长度 + 内容法 LengthFieldPrepender LengthFieldBasedFrameDecoder

至于定长法和分割符法没有编码对应的类,那是因为太简单了,Netty 都懒得实现了。

为什么解码类后面都是 *FrameDecoder 呢?那是因为被解码之后的消息又叫作一帧一帧的消息,所以称为 “帧解码器”。

那么,在 Netty 中如何使用它们呢?

其实,使用方法非常简单,只需要在 childHandler 中添加一个解码器就可以了,比如,以 FixedLengthFrameDecoder为例:

public final class EchoServer {static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));public static void main(String[] args) throws Exception {// 省略其它代码.childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();p.addLast(new LoggingHandler(LogLevel.INFO));// 添加固定长度解码器,长度为3p.addLast(new FixedLengthFrameDecoder(3));p.addLast(echoServerHandler);}});// 省略其它代码}
}

Netty如何解决粘包半包问题相关推荐

  1. 三、Netty的粘包半包问题解决

    一.定义 TCP 传输中,客户端发送数据,实际是把数据写入到了 TCP 的缓存中,粘包和半包也就会在此时产生.客户端给服务端发送了两条消息ABC和DEF,服务端这边的接收会有多少种情况呢?有可能是一次 ...

  2. TCP 粘包半包 netty 编解码 三者关系

    1 何为粘包 / 半包? 对方一次性接收了多条消息这种现象,我们就称之为 粘包现象. 对方多次接收了不完整消息这种现象,我们就称之为 半包现象. 粘包的原因: 发送方发送的消息 < 缓冲区大小 ...

  3. Netty粘包/半包问题解析

    目录 一.什么是粘包/半包问题 二.TCP粘包/半包发生的原因 三.粘包/半包解决办法 四.Netty中粘包/半包解决示例 1. 采用固定长度数据包编解码方式 2. 采用特殊字符作为边界字符编解码方式 ...

  4. Netty框架之TCP粘包/半包解决方案

    Netty框架之TCP粘包/半包解决方案 一.TCP粘包 二.TCP半包 三.TCP粘包/半包解决方案 1.FixedLengthFrameDecoder定长解析器 2.LineBasedFrameD ...

  5. 网络:什么是TCP粘包/半包?怎么解决这个问题

    在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接.在TCP的socket编程中,发送端和接收端都有成对的socke ...

  6. netty——黏包半包的解决方案、滑动窗口的概念

    黏包半包 滑动窗口 在深入理解黏包半包问题之前,先了解TCP的一个知识点--滑动窗口 我们都指定tcp是一种可靠的传输协议,这主要是因为在tcp中客户端给服务器端发送一条消息,要等待服务器端的应答,如 ...

  7. websocket是否需要处理粘包半包问题分析

    结论: ​ 不需要. 背景: ​ 公司通信涉及到websocket相关,我们都知道websocket是基于tcp的,而tcp是面向字节流的,是需要处理粘包半包问题的.那么websocket是否需要处理 ...

  8. Http 调用netty 服务,服务调用客户端,伪同步响应.ProtoBuf 解决粘包,半包问题.

    实际情况是: 公司需要开发一个接口给新产品使用,需求如下 1.有一款硬件设备,客户用usb接上电脑就可以,但是此设备功能比较单一,所以开发一个服务器程序,辅助此设备业务功能 2.解决方案,使用Sock ...

  9. 什么是粘包和拆包,Netty如何解决粘包拆包?

    Netty粘包拆包 TCP 粘包拆包是指发送方发送的若干包数据到接收方接收时粘成一包或某个数据包被拆开接收. 如下图所示,client 发送了两个数据包 D1 和 D2,但是 server 端可能会收 ...

最新文章

  1. 【OpenCV 4开发详解】Sobel算子
  2. R语言ggplot2可视化:ggplot2可视化水平堆叠条形图、并且在每个堆叠条形图的内部居中添加百分比文本标签信息
  3. css3绘制环形_HTML5 + CSS3 gt;gt;gt; 015
  4. python 多态 知乎_Python函数接口的一些设计心得
  5. PHP----------线程安全和非线程安全的介绍
  6. unity 调用 .dll 或 .so时遇到的问题
  7. SAP系统财务模块的集团公司处理模式
  8. Java 实现排序
  9. Replace Delegation with Inheritance(以继承取代委托)
  10. 前端框架:AntdUI 文档入门
  11. 千博HTML5自适应企业网站系统 v2021 Build0622
  12. 字符流的抽象类 java
  13. vue node php,vue node 是什么
  14. nohup rabbitmq python
  15. jsp和php对比如何,jsp和php对比哪个更好
  16. java keytool工具详解
  17. i5 7200u 计算机专业,i5 7200U理论性能对比_笔记本评测-中关村在线
  18. 【领域建模】UML类图工具推荐
  19. 五子棋游戏初次编写尝试
  20. 咖啡馆html报告,咖啡屋调查报告.ppt

热门文章

  1. iOS 微信、支付宝、银联、Paypal 支付组件封装
  2. Numpy 基本用法
  3. 百分点:利用大数据做智慧商业
  4. 【全面解析Mock】Mock在单元测试中扮演一个什么角色?
  5. Openlab实验平台实验--Mininet Mac地址学习实验
  6. 部编版是什么版本_什么叫做部编版?部编版和人教版的区别是什么?
  7. 腾讯地图api_特斯拉选择百度地图背后的过往和野心
  8. 移动端---混合开发1 + 支付相关操作(手机app支付、网页支付)--支付流程
  9. 关闭windows10测试模式水印
  10. 2019“欢乐春节·魅力中国”为洛杉矶增添“中国年味儿”