3.TCP粘包/拆包问题
1.TCP粘包/拆包问题说明
- TCP是个流协议。底层并不了解上层业务数据的具体含义,所以一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送。这个是粘包/拆包问题。
- 所以服务端TCP接收滑窗非常小,而数据包非常大,很有可能要分多次才能接收完全,期间发生多次拆包。
2.TCP粘包/拆包发生的原因
- 应用程序write写入的字节大小大于套接口发送缓冲区大小。
- 进行MSS大小的TCP分段。
- IP层要传的数据大于MTU进行IP分片。
3.粘包问题的解决策略
- 由于底层TCP无法理解上层的业务数据,所以只能通过上层的应用协议栈设计来解决:
(1)消息定长。
(2)在包尾增加回车换行符进行分割。例如FTP协议。
(3)将消息分为消息头和消息体,消息头中包含表示消息总长度的字段,通常使用int32.
(4)更复杂的应用层协议。
4.利用LineBasedFrameDecoder和StringDecoder解决粘包/拆包问题
- 对于使用者,只要将支持半包解码的Handler添加到ChannelPipeline中即可,不需要写额外的代码
- LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf的可读字节,判断看是否有换行字节,如果有,就以此位置为结束位置。从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标志的解码器,支持携带结束符或者不携带结束符2种解码方式,同时支持配置单行的最大长度。
- StringDecoder就是将接收到的对象转换成字符串,然后继续调用后面的Handler。
4.分隔符和定长解码器的应用
- DelimiterBasedFrameDecoder:通过自定义分隔符来区分数据包。
- FixedLengthFrameDecoder:无论一次接受到多少数据报,它都会按照构造函数中设置的固定长度进行解码,如果是半包消息,FixedLengthFrameDecoder会缓存半包消息并等待下个包到达后进行拼包,直到读取到一个完整的包。
5.序列化
1.Java序列化
- 基于Java提供的对象输入/输出流,可以直接把Java对象作为可存储的字节数组写入文件,也可以传输到网络上。基于JDK默认的序列化机制可以避免操作底层的字节数组。
- Java序列化的缺点:
(1)无法跨语言。
(2)序列化后的码流太大
(3)序列化性能太低