使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

网络 网络管理
我们使用TCP协议在传输数据的时候,如果数据块比较大,就会考虑将其切分。把一个大的数据包进行切割成一个个小的数据包发送。这时候就会遇到拆包和粘包的问题。

 [[311931]]

这篇文章会按照以下步骤进行讲解,希望对你有所收获:

1、什么是TCP粘包拆包2、Netty中粘包问题的问题重现3、Netty中粘包问题的解决方案

OK,在你心中有这么一个基本的脉络之后就可以开始今天的文章了。本系列所有的文章都会给出完整的代码,且在电脑上真实运行了一遍,确保无误。

一、什么是TCP拆包和粘包

我们使用TCP协议在传输数据的时候,如果数据块比较大,就会考虑将其切分。把一个大的数据包进行切割成一个个小的数据包发送。这时候就会遇到拆包和粘包的问题。

比如说在这里客户端发送了两个数据包D1和D2到服务端,在传输的时候就可能会遇到下列问题:

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

通过上面这张图相信你基本上能够理解了。不过我们在这里还是需要稍微解释一下:

情况1:D1和D2正常发送,每次发送一个整包。

情况2:D1数据包比较大,D2比较小。第一次发送D1的一部分,第二次发送D1剩下的和D2整包。这叫拆包。

情况2:D1和D2数据包都比较小,一次发送两个整包,这就叫做粘包。

情况4:D1数据包比较小,D2比较大。第一次发送D1整包和D2一部分,第二次发送D2剩下的。这叫拆包。

情况5:D1和D2数据包都比较大,这时候分开发。

为什么会出现这样的问题呢?想要解释清楚,就必须要考虑到计算机网络的相关知识了,TCP在接受数据的时候,有一个滑动窗口来控制接受数据的大小,这个滑动窗口你就可以理解为一个缓冲区的大小。缓冲区满了就会把数据发送。数据包的大小是不固定的,有时候比缓冲区大,有时候小。这时候就会出现上面的现象。

下面我们使用代码来重现这个现象。

二、问题重现

1、前提准备

我们是基于Springboot开发的,因此还是和上一节一样,首先创建一个Springboot的web工程,添加一下依赖:

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

如果你没有使用maven,下载相关jar包,直接导入IDE中即可。

2、服务端代码开发

步骤一:创建server类

这个server类,在上一篇文章中提到,是一个模板类,直接拿来用即可。

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

在上面的这个代码中同样我们最主要的是关注ServerUAVHandler的实现。

步骤二:Handler的实现

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

在这个类中,使用channelRead方法来读取客户端发送过来的信息。

(1)首先定义了一个counter,用于计算客户端发送了多少条消息。

(2)在channelRead内部,首先将msg转化为ByteBuf。

(3)将buf的数据转化为字节byte

(4)将buf的字节数据转化为String类型,然后输出。

(5)使用ctx的writeAndFlush方法,每收到一个客户端的数据,给对方回复一个A。别忘了还有一个换行符。

在上面的这个代码中,最主要的就是服务端每收到一条客户端的信息,就给其回复一条。也就是说客户端和服务端的消息数量应该是一样的。

3、客户端代码开发

步骤一:创建client类

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

同样的代码的逻辑在上一篇文章中已经说了,我们还是最关注的事件处理类Handler。

步骤二:Handler实现

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

这个客户端的Handler看起来有点多,一共有两个方法,channelActive和channelRead。

(1)channelActive里面使用for循环给服务器发送了100条,我爱你。每次发送还有在末尾添加一个换行符。

(2)channelRead里面接受服务器返回的消息。

按道理来讲,客户端给服务端发送了100条数据,那么服务端也会返回回来100条。我们来验证一下。

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

这里输出的是服务端的信息,从上面的输出结果你就会发现,其实客户端的“我爱你”都被黏在了一块。本来100条但是现在却只有17条了,这就是发生了粘包现象。

如何来解决呢?下面我们看看。

三、粘包问题解决

解决的思路很简单,也就是每次发送一个数据包的时候,添加一个标识符,读的时候一直读到这个标识符才表示一个完整的数据包。在上面我们添加的是line.separator,也就是换行符“\n”。

1、服务端server类更改。

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

2、服务端Handler类更改

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

3、客户端Client更改

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

4、客户端Handler更改

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

客户端和服务端改的地方都一样,不过还是贴了出来,现在我们再运行一波。

 

使用Netty通信时,遇到TCP粘包拆包问题如何解决?答案如此简单

 

看到没是不是很神奇。我们来分析一下我们都修改了什么。

好像我们就只是在server和client类添加了两个类,一个是LineBasedFrameDecoder,一个是StringDecoder,其他的都是直接删除,这两个类有什么作用呢?

(1)LineBasedFrameDecoder的作用是在读取数据的时候,一直读到是否含有换行符“\n”或者是“\r\n”。如果读到了就表示该结束了。因此就拿到了这一行的数据包。

(2)StringDecoder用于对之前LineBasedFrameDecoder读取的这一行数据包进行解码。将对象转换为字符串。

OK,好像他们俩搭配,干活真不累,现在我们终于可以解决粘包的问题了,但是同时也出现了一个新的问题,那就是如果我们的标识符不是换行符“\n”或者是“\r\n”又该怎么办呢?幸好Netty同样为我们提供了几种其他的解码器,叫做DelimiterBasedFrameDecoder和FixedLengthFrameDecoder,前面这个可以自动完成以分隔符做结束标志的消息,后面这个可以自动完成对定长消息的解码。都可以解决粘包拆包问题。

 

责任编辑:武晓燕 来源: 今日头条
相关推荐

2021-07-15 10:35:16

NettyTCPJava

2020-12-23 07:53:01

TCP通信Netty

2019-10-17 11:06:32

TCP粘包通信协议

2021-03-09 22:30:47

TCP拆包协议

2020-10-15 18:31:36

理解Netty编解码

2022-04-28 08:38:09

TCP协议解码器

2019-08-15 07:43:38

TCP网络协议丢包

2021-01-13 10:18:29

SocketNetty粘包

2020-03-10 08:27:24

TCP粘包网络协议

2019-10-25 00:32:12

TCP粘包Netty

2019-10-24 07:35:13

TCP粘包Netty

2021-10-08 09:38:57

NettyChannelHand架构

2020-04-09 11:08:30

PyFlinkJAR依赖

2015-04-01 10:26:32

开发中文乱码问题

2022-08-01 07:07:15

粘包半包封装

2024-01-10 09:44:11

MySQL死锁

2009-12-04 13:31:24

PHP缺少JSON包

2020-10-19 07:51:25

Linux软件

2009-12-10 14:19:41

配置静态路由

2012-09-05 11:09:15

SELinux操作系统
点赞
收藏

51CTO技术栈公众号