|
|
|
@ -20,23 +20,17 @@ TCP 是个 “流” 协议,所谓流,就是没有界限的一串数据。TC
|
|
|
|
|
2. 进行 MSS 大小的 TCP分段;
|
|
|
|
|
3. 以太网帧的 payload 大于 MTU 进行 IP分片。
|
|
|
|
|
|
|
|
|
|
### 粘包问题的解决策略
|
|
|
|
|
### 粘拆包问题的解决策略
|
|
|
|
|
由于底层的 TCP 无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下。
|
|
|
|
|
1. 消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格;
|
|
|
|
|
2. 在包尾增加回车换行符进行分割,例如FTP协议;
|
|
|
|
|
3. 将消息分为消息头和消息体,消息头中包含表示消息总长度 (或者消息体长度) 的字段,通常设计思路为消息头的第一个字段使用 int32 来表示消息的总长度;
|
|
|
|
|
1. 固定消息长度,例如,每个报文的大小为 固定长度200字节,如果不够,空位补空格;
|
|
|
|
|
2. 在包尾使用 “回车换行符” 等特殊字符,作为消息结束的标志,例如FTP协议,这种方式在文本协议中应用比较广泛;
|
|
|
|
|
3. 将消息分为消息头和消息体,在消息头中定义一个 长度字段Len 来标识消息的总长度;
|
|
|
|
|
4. 更复杂的应用层协议。
|
|
|
|
|
|
|
|
|
|
介绍完了 TCP粘包/拆包 的基础,下面我们来看看 Netty 是如何使用一系列 “半包解码器” 来解决 TCP粘包/拆包问题的。
|
|
|
|
|
|
|
|
|
|
## 利用 Netty的解码器 解决 TCP粘拆包问题
|
|
|
|
|
TCP 以流的方式进行数据传输,上层应用协议为了对消息进行区分,往往采用如下4种方式。
|
|
|
|
|
1. 固定消息长度,累计读取到长度总和为 定长Len 的报文后,就认为读取到了一个完整的消息,将计数器置位,重新开始读取下一个数据报;
|
|
|
|
|
2. 将 “回车换行符” 作为消息结束符,例如 FTP协议,这种方式在文本协议中应用比较广泛;
|
|
|
|
|
3. 将特殊的分隔符作为消息的结束标志,回车换行符是特殊的结束分隔符之一;
|
|
|
|
|
4. 在消息头中定义一个 长度字段Len 来标识消息的总长度。
|
|
|
|
|
|
|
|
|
|
Netty 对上面4种应用做了统一的抽象,提供了4种解码器来解决对应的问题。有了这些解码器,用户不需要自己对读取的报文进行人工解码,也不需要考虑TCP的粘包和拆包。
|
|
|
|
|
根据上面的 粘拆包问题解决策略,Netty 提供了相应的解码器实现。有了这些解码器,用户不需要自己对读取的报文进行人工解码,也不需要考虑TCP的粘包和拆包。
|
|
|
|
|
|
|
|
|
|
### LineBasedFrameDecoder 和 StringDecoder 的原理分析
|
|
|
|
|
为了解决 TCP粘包 / 拆包 导致的 半包读写问题,Netty 默认提供了多种编解码器用于处理半包,只要能熟练掌握这些类库的使用,TCP粘拆包问题 从此会变得非常容易,你甚至不需要关心它们,这也是其他 NIO框架 和 JDK原生的 NIO API 所无法匹敌的。对于使用者来说,只要将支持半包解码的 Handler 添加到 ChannelPipeline对象 中即可,不需要写额外的代码,使用起来非常简单。
|
|
|
|
|