熟悉TCP变成的可以知道,无论是客户端还是服务端,但我们读取或者发送消息的时候,都需要考虑TCP底层粘包/拆包机制,下面我们先看一下TCP 粘包/拆包和基础知识,然后模拟一个没有考虑TCP粘包/拆包导致功能异常的案例,最后,通过正确的例程来谈谈Netty是如何实现的。

主要内容:

  • TCP粘包/拆包的基础知识

  • 没考虑TCP粘包/拆包的问题案例

  • 使用Netty解决读半包问题

1、TCP粘包/拆包

TCP是个“流“协议,所谓流,就是没有界限的一串数据。TCP底层并不知道上层业务逻辑,它会根据TCP缓冲区的实际情况进行包的拆分,所以在业务上认为,一个完整的包可能会被拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包/拆包的问题。

2、TCP粘包/拆包发生的原因

问题产生的原因有三个:如下

  • 应用程序write写入的字节大小大于套接口发送缓冲区大小;

  • 进行MSS大小的分段;

  • 以太网帧的payload大于MTU进行IP分片;

备注:mtu是网络传输最大报文包。mss是网络传输数据最大值。

3、粘包问题的解决策略

由于底层TCP无法理解上层业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下:

  • 消息定长,例如每个报文的大小长度200字节,如果不够,不空格;

  • 在包尾增加回车换行符,例如FTP协议;

  • 将消息分为消息头和消息体,消息头包含表示消息总长度的字段,通常设计思路为消息头的第一个字段使用int32来表示消息的总长度;

  • 更复杂的设计协议;

介绍完了TCP粘包/拆包的基础知识后,我们看一下Netty是如何解决半包问题的,是如何使用Netty的半包解码器来解决TCP粘包/拆包问题。

4、未考虑TCP粘包/拆包问题出现的功能异常

TimeServer的改造(可以查看上一篇文章中的netty客户端-服务端的实现):

每读到一条消息后,就计数一次,然后发送应答消息给服务端。

TimeClient端的改造:

运行结果(服务端接收指令):

The time server receive order : QUERY TIME ORDER

此处省略57行。。。。。。。

QUERY TIME ORD ; the counter is :1

The time server receive order :

此处省略43行。。。。。。。

QUERY TIME ORDER ; the counter is :2

运行结果(客户端接收响应):

Now is : BAD ORDER

BAD ORDER

; the counter is : 1

原因分析:服务端运行结果表明它只接收到两条消息,第一条包含57条“QUERY TIME ORDER”指令,第二天包含了43条指令,总数100条,我们期望的也是100条,但是计数只有两条,所有发生TCP粘包,按照设计初衷,客户端应该收到100响应,但实际上只收到了1条,不难理解,客户端也发生了粘包,一条应答消息中包含两条“BAD ORDER”指令的消息。

5、通过LineBasedFrameDecoder解决TCP粘包问题

为了解决TCP粘包/拆包导致的半包读写问题,Netty默认提供了多种编解码器用于处理半包,这是其他NIO框架和JDK原生的NIO API不能匹敌的。

直接上代码

TimeServer:

在原来的TimeServerHandler之前增加了两个解码器:LineBasedFrameDecoder、StringDecoder

TimeServerHandler:

TimeClient:

TimeClientHandler:

支持TCP粘包的运行结果:

服务端:

The time server receive order : QUERY TIME ORDER ; the counter is :1

此处省略92条。。。。。。

The time server receive order : QUERY TIME ORDER ; the counter is :100

客户端:

Now is : Tue Aug 21 15:15:21 CST 2018 ; the counter is : 1

此处省略92条。。。。。。

Now is : Tue Aug 21 15:15:21 CST 2018 ; the counter is : 100

6、LineBasedFrameDecoder、StringDecoder原来分析

LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断是否有“\n“或者“\r\n”,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标记的解码器,

StringDecoder非常简单,就是将接收到的对象转换成字符串,然后继续调用后面的Handler,

总结:LineBasedFrameDecoder + StringDecoder组合就是按行切换的文本解码器,它被设计用来支持TCP的粘包、拆包。

疑问:

1、如果发送的消息不是以换行符结束的怎么办?

2、靠消息头中的长度字段来分包的怎么办?

这样的话是否需要自己写半包解码器,答案是否定的,Netty 提供了多种支持 TCP粘包、拆包的解码器,用来满足需求,下面的文章中会详细介绍《分隔符解码器》《定长解码器》,因为它在项目中使用非常广泛,所以单独去分享这一知识点。

Netty 粘包/拆包应用案例及解决方案分析的更多相关文章

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

    目录: 粘包 & 拆包及解决方案 ByteToMessageDecoder 基于长度编解码器 基于分割符的编解码器 google 的 Protobuf 序列化介绍 其他的 前言 Netty 作 ...

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

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

  3. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  4. TCP粘包/拆包(Netty权威指南)

    无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个“流”协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片 ...

  5. Netty(三)TCP粘包拆包处理

    tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D ...

  6. Netty2:粘包/拆包问题与使用LineBasedFrameDecoder的解决方案

    什么是粘包.拆包 粘包.拆包是Socket编程中最常遇见的一个问题,本文来研究一下Netty是如何解决粘包.拆包的,首先我们从什么是粘包.拆包开始说起: TCP是个"流"协议,所谓 ...

  7. 第四章 TCP粘包/拆包问题的解决之道---4.2--- 未考虑TCP粘包导致功能异常案例

    4.2 未考虑TCP粘包导致功能异常案例 如果代码没有考虑粘包/拆包问题,往往会出现解码错位或者错误,导致程序不能正常工作. 4.2.1 TimeServer 的改造 Class : TimeServ ...

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

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

  9. Netty使用LineBasedFrameDecoder解决TCP粘包/拆包

    TCP粘包/拆包 TCP是个”流”协议,所谓流,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TC ...

随机推荐

  1. 【转】基于easyui开发Web版Activiti流程定制器详解(一)——目录结构

    题外话(可略过): 前一段时间(要是没记错的话应该是3个月以前)发布了一个更新版本,很多人说没有文档看着比较困难,所以打算拿点时间出来详细给大家讲解一下,由于本人平时要工作还要陪老婆和孩子而且还经营着 ...

  2. python第二十一课——str中的常用函数(重要)

    演示str中常用的一些函数: 1.join():将容器对象以某种特定的格式(字符串)进行拼接组合,最后以字符串的形式返回 lt=['i','love','you','very','much'] str ...

  3. Spring-IOC bean 生命周期之 Lifecycle 钩子

    Lifecycle callbacks Initialization callbacks.Destruction callbacks 要与容器的bean生命周期管理交互,即容器在启动后和容器在销毁前对 ...

  4. jQuery鼠标悬停

    (function ($) { $.fn.hoverdir = function(options){ var options = $.extend({ choice : ".cove&quo ...

  5. ethereumjs/ethereumjs-common-3-test

    查看test能够让你更好滴了解其API文档的使用 ethereumjs-common/tests/chains.js const tape = require('tape') const Common ...

  6. Burpsuite-Intruder-xssValidator(XSS检测)基础学习

    这次总结的是使用Burp+PhantomJS进行xss测试. 首先,当然是xss测试的环境配置了. 1. PhantomJS安装及Path配置:自己找资料吧. phantomjs -v验证是否成功安装 ...

  7. nginx学习要点记录

    IO多路复用: 1.轻量 2.cpu亲和性:把CPU核心和nginx工作进程绑定,把每个worker进程固定在一个CPU上执行,减少切换cpu的cache miss,获得更好的性能 3.IO多路复用e ...

  8. P1070 道路游戏

    题目描述 小新正在玩一个简单的电脑游戏. 游戏中有一条环形马路,马路上有 n 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针顺序依次将这 n 个机器人工厂编 ...

  9. Verilog HDL与C语言的比较

    Verilog HDL与C语言的比较 Verilog HDL是在C语言的基础上发展起来的,因而它保留了C语言所独有的结构特点.   为便于对Verilog HDL有个大致的认识,在这里将它与C语言的异 ...

  10. js随笔记录

     1.当我们尝试优化一段程序的时候,必须要同时了解语言本身和运行环境就比如说,可能教科书上写移位操作比乘法运算要快,但是这是因为CPU指令的问题,所以对于C语言成立,对于跑在VM上的语言来说则不一定了 ...