[toc]
1.什么是TCP粘包和半包?为什么会出现粘包和半包?
粘包:
粘包半包 混合双打:
首先TCP是可靠有序的。tcp序列号确认号,失败重传机制等都保证了他不会出现乱序和丢失报文的现象。但是因为TCP是面向字节流的,消息无边界,所以面临这样的问题。
2.什么情况会出现粘包和半包?
上面也解释了一部分,下面具体看什么情况:
粘包
- 发送方每次写入的数据 < 套接字缓存区大小
- 接收方接受套接字缓冲区不够及时
半包
- 发送方每次写入数据 > 套接字缓冲区大小
- 发送的数据大于协议MTU(最大传输单元,必须拆包)
3.如何解决粘包和半包?
上面讲,出现粘包和半包的原因就是TCP流式传输,消息无边界,找出消息边界解决问题,(简单的来说,就是让接收端知道一次需要read几个字节是一段话就ok)
长 = 长链接,每次数据封装为帧
方式\比较 | 找到消息边界方式 | 优点 | 缺点 |
---|---|---|---|
短连接/一个请求一个短连接 | 连接释放之前的信息为传输消息 | 简单 | 效率低下 |
长(固定长度) | 读取固定长度 | 简单 | 可能会空间浪费 |
长(分隔符) | 分隔符 | 简单 | 内容中有分隔符需要转义 |
长(固定长度字段存内容的长度消息) | 协议头中制定消息体的长度 | 优秀 上面问题都不存在 | 长度有限制(表示长度的这个几个字节本身能表达的数字是有限的) |
长(其他方式) | 每种都不同,列如json可以看{}是否成对了 |
==协议 = 协议头[定长] + (Object)序列化后的消息体==
一般我们实现通心层都需要定义一个通信层协议,协议头固定长度,协议头中包含了消息体的长度。这样我们接收端不就知道了消息界限了吗。
- 我们把 “找到消息边界方式” 视为第一层解码
- “消息体反序列化”视为第二层解码
4.Netty中如何实现?
- 我们把 “找到消息边界方式” 视为第一层解码
- “消息体反序列化”视为第二层解码
Netty中为什么不合二为一?
- 没有分层,不够清晰
- 耦合性高,不容易切换方案
第一次解码器
ByteToMessageDecoder
- 原始数据量->用户数据
方式\支持 | 解码 | 编码 |
---|---|---|
固定长度 | FixedLengthFrameDecoder | 不内置 |
分隔符 | DelimiterBaseFrameDecoder | 太简单不内置 |
固定长度字段,存内容的长度信息 | LengthFieldBaseFrameDecoder | LengthFieldPrepender |
第二次解码器
MessageToMessageDecoder<T>
- 用户数据->java Object
如何选择?
- 空间大小(根据数据量变)
- 编解码速度(根据数据量变)
- 可读性
- 多语言支持