当前位置: 移动技术网 > IT编程>软件设计>架构 > Netty 粘包/拆包应用案例及解决方案分析

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

2018年08月22日  | 移动技术网IT编程  | 我要评论

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

主要内容:

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

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

  • 使用netty解决读半包问题

 

1、tcp粘包/拆包

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

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

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

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

  • 进行mss大小的分段;

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

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

https://img3.mukewang.com/5b7ba6030001819a08230455.jpg

3、粘包问题的解决策略

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

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

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

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

  • 更复杂的设计协议;

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

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

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

https://img1.mukewang.com/5b7badcc0001a03413320886.jpg

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

timeclient端的改造:

https://img2.mukewang.com/5b7bb0c20001cdb211660848.jpg

https://img2.mukewang.com/5b7bb0df0001caaf10360573.jpg

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

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:

https://img3.mukewang.com/5b7bb71000018a2610730414.jpg

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

 

timeserverhandler:

https://img2.mukewang.com/5b7bbafd0001168013390526.jpg

timeclient:

https://img3.mukewang.com/5b7bb9b90001e18310300693.jpg

timeclienthandler:

https://img2.mukewang.com/5b7bbbb500017c0311540389.jpg

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

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网