拆包粘包的场景分析
粘包和拆包的场景主要有下图几种:
客户端首先和服务端建立 TCP 连接,然后客户端向服务端发送请求,我们假设客户端发送的请求就是手写 RPC 项目的 NettyRpcRequest, 我们假设一个 NettyRpcRequest 的字节大小为128,客户端向服务端发送了三个数据包:
- 数据包1:粘包现象。数据包1包涵两个完整的 rpcRequest 字节数组包括 rpcRequest0 和 rpcRequest1。
- 数据包2:拆包现象。数据包2只包涵 rpcRequest2 从0到65的字节,也就是说只有部分的 rpcRequest2 的数据。
- 数据包3:同时具有粘包和拆包现象。数据包3包涵了 rpcRequest2 从66到127的字节,同时包涵了全部的 rpcRequest3 的字节。
而服务端收到每一个数据包的时候是无法判断数据包内的情况的,也就是说TCP 协议是面向字节流的而不是语义。我们再看一个代码例子,这样会更加有说服力。
拆包粘包的例子
配套的源码源代码地址:github.com/sean417/net… 的 netty-rpc 模块。
下面的 sticky 包做了一个模拟粘包的例子:
首先,我们看看客户端的代码:
客户端会发送500个带有换行符的字节数组请求 pingBytes,每个 pingBytes 的末尾都会有一个换行符。
然后,再看一下 服务端的接收请求的代码:
分别执行服务端启动类 NettyStickyPacketServer 和 客户端启动类 NettyStickyPacketClient。 服务端输出结果:
大家可以看出来既有拆包的情况也有粘包的情况。拆包和粘包对接收方来说必须要解决的问题,因为接收方是要把字节流反序列化为业务对象的,而业务对象是由语义的,需要把没有语义的字节流转换为有语义的业务对象。那么我们应该怎么处理呢?
粘包拆包的解决方案
主要有两种解决方案:
一个请求由请求长度和请求体组成
反序列化类 RpcDecoder: