欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

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

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

主要内容:

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

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

  • 使用netty解决读半包问题

 

1、tcp粘包/拆包

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

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

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

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

  • 进行mss大小的分段;

  • 以太网帧的payload大于mtu进行ip分片; 

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

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

3、粘包问题的解决策略

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

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

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

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

  • 更复杂的设计协议;

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

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

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

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

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

timeclient端的改造:

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

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

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

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:

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

    在原来的timeserverhandler之前增加了两个解码器:linebasedframedecoder、stringdecoder

 

timeserverhandler:

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

timeclient:

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

timeclienthandler:

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

支持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粘包、拆包的解码器,用来满足需求,下面的文章中会详细介绍《分隔符解码器》《定长解码器》,因为它在项目中使用非常广泛,所以单独去分享这一知识点。