TCP协议下的粘包与拆包,如何解决
TCP协议下的粘包与拆包,如何解决一、粘包、拆包1.1 粘包原因1.1.1 滑动窗口1.1.2 Nagle算法1.1.3 应用层原因1.2 拆包原因1.2.1 滑动窗口1.2.2 MSS限制1.2.3 应用层原因1.2 Netty提供的解决方案二、自定义协议解决粘包、拆包2.1 自定义协议要素
一、粘包、拆包
1.1 粘包原因
1.1.1 滑动窗口
因为TCP/IP在起初,所有的请求是串行化的,之后做成了滑动窗口的概念。那么在接收方,如果接收不及时且窗口大小足够大,就可能出现粘包的情况。
1.1.2 Nagle算法
因为每次数据发送的时候,都需要加上消息头等特殊数据,TCP与IP协议分别会加20Byte数据,因此哪怕只是发送1Byte数据,最终接收方还是会接收到41Byte;在这样的背景下,Nagle算法可能会将多个数据包合并在一起发送。
1.1.3 应用层原因
接收方ByteBuf设置太大(Netty默认为1024Byte),因此如果客户端发送的报文都非常小,抛开上述1.1.1.1中的原因不谈,光在Netty这一处也非常容易出现粘包现象
1.2 拆包原因
1.2.1 滑动窗口
假设接收方的窗口只剩128Bytes,发送方的报文大小是256Bytes,这时放不下了,只能先发送前128Bytes,等待ack后,窗口有剩余空间了才会发送剩余部分,这就导致了拆包
1.2.2 MSS限制
当发送数据超过MSS限制后,会将数据切分发送,而这个MSS是根据不同类型网卡的限制来看,譬如某笔记本网卡可以发送1500Byte,除去一次数据包中的TCP/IP固定40Byte外,真正的数据也只能发送1460Byte。
1.2.3 应用层原因
Netty中ByteBuf设置的大小小于数据包大小。
1.2 Netty提供的解决方案
-
FixedLengthFrameDecoder (消息体定长处理)
其继承了ByteToMessageDecoder,帮助开发人员写了decode方法
-
DelimiterBasedFrameDecoder、LineBasedFrameDecoder(结束/开始标记)
其继承了ByteToMessageDecoder,帮助开发人员写了decode方法
-
DelimiterBasedFrameDecoder可以自定义数据包分割方式
-
LineBasedFrameDecoder默认以\n\r进行分割,一个是Windows OS,另一个是Linux OS下的换行符
-
-
LengthFieldBasedFrameDecoder
相比于前三种Netty内置的解决方案,这一种方式更加灵活一些,但其灵活度仍然无法与自定义协议相匹敌
-
除去Redis、Http协议之外采用自定义协议,具体见第二章节
- Redis协议缺点:需要有大量的换行符,导致数据包数据不紧凑,浪费空间
二、自定义协议解决粘包、拆包
粘包、拆包是在本文第一章节说明,但由于自定义协议方式需要注意的细节很多有必要单独放在一个章节做出说明。
2.1 自定义协议要素
-
魔术
用来第一时间判定该数据包是否有效,如果Netty接受到魔术不正确的数据包可以直接丢弃
-
版本号
可以支持协议的升级
-
序列化算法
消息正文采用哪种方式序列化,需要对应的使用反序列化,常见的序列、反序列化方式有:json、protobuf、hessian、jdk方式
-
指令类型
该字段与业务相关
-
请求序号
为了双工通信,提供异步能力
-
正文长度
是指下面所说的消息正文经过序列化后且转换成字节数组的长度
-
消息正文
这里的正文是经过序列化后的字节数组
未完待续... ...
网友评论