粘包、半包
粘包原因
1、应用层:接收方 ByteBuf 设置太大(Netty 默认 1024)
2、传输层:滑动窗口中缓冲多个报文
3、Nagle 算法
半包原因
1、应用层:接收方 ByteBuf 小于实际发送数据量
2、传输层
(1)接收方窗口中无法容纳发送方的全部报文,接收最后剩余部分,接收窗口仍有容量
(2)当发送的数据超过 MSS 限制后,会将数据切分发送,造成半包
粘包、半包本质:TCP 是流式协议,消息无边界
1、滑动窗口
(1)TCP 以一个段(segment)为单位,每发送一个段,就需要进行一次确认应答(ack)处理
(2)缺点:包的往返时间越长,性能就越差
(3)窗口大小决定无需等待应答,而可以继续发送的数据最大值
(4)窗口实际就起到一个缓冲区的作用,同时也能起到流量控制的作用
2、MSS 限制
(1)MTU(Maximum Transmission Unit):数据链路层对一次能够发送的最大数据有限制
(2)不同的链路设备的 MTU 值不同,如:以太网的 MTU 是 1500、FDDI(光纤分布式数据接口)的 MTU 是 4352、本地回环地址的 MTU 是 65535(本地测试不走网卡)
(3)MSS:Maximum Segment Size,最大段长度,是 MTU 去除 TCP 头、IP 头后,剩余能够作为数据传输的字节数
(4)IPv4:TCP 头占用 20 bytes,IP 头占用 20 bytes,以太网 MSS 值为 1500 - 40 = 1460
(5)TCP 在传递大量数据时,会按照 MSS 大小将数据进行分割发送
(6)MSS 的值在三次握手时,通知对方自己 MSS 的值,然后在两者之间选择一个小值作为 MSS
3、Nagle 算法
(1)即使发送一个字节,也需要加入 TCP 头、IP 头
(2)目的:提高网络利用率,TCP 希望尽可能发送足够大的数据
(3)发送端即使还有应该发送的数据,但如果这部分数据很少的话,则进行延迟发送
(4)如果 SO_SNDBUF 的数据达到 MSS,则需要发送
(5)如果 SO_SNDBUF 中含有 FIN(表示需要连接关闭),这时将剩余数据发送,再关闭
(6)如果 TCP_NODELAY = true,则需要发送
(7)已发送的数据都收到 ack 时,则需要发送
(8)上述条件不满足,但发生超时(一般为 200ms),则需要发送
(9)除上述情况,延迟发送
短连接
1、客户端每次向服务器发送数据以后,就与服务器断开连接,此时的消息边界为连接建立到连接断开
2、无需使用滑动窗口等技术来缓冲数据,则不会发生粘包现象
3、但如果一次性数据发送过多,接收方无法一次性容纳所有数据,还是会发生半包现象,所以短连接无法解决半包现象
定长解码器
1、客户端
(1)向服务器约定一个最大长度,保证客户端每次发送的数据长度都不会大于该长度
(2)若发送数据长度不足,则需要补齐至该长度
2、服务器
(1)接收数据时,将接收到的数据按照约定的最大长度进行拆分
(2)即使发送过程中产生粘包,可以通过定长解码器将数据正确地进行拆分
(3)使用 FixedLengthFrameDecoder 对数据进行定长解码
行解码器
1、通过分隔符对数据进行拆分,解决粘包、半包
2、LineBasedFrameDecoder
(1)一个解码器,将收到的多个 ByteBuf 在行尾分割
(2)\n、\r\n 都可以处理
(3)字节流被期望为 UTF-8 / ASCII,目前的实现使用直接字节到字符的转换,然后将该字符与一些低范围的 ASCII 字符进行比较,如:\n 或 \r,UTF-8 没有使用低范围的 [0...0x7F] 字节值来表示多字节的代码点,因此完全被这个实现所支持
3、DelimiterBasedFrameDecoder
(1)与相比 LineBasedFrameDecoder,更通用
(2)一个解码器,通过一个或多个分隔符分割收到的多个 ByteBuf
(3)它对解码以定界符(如:NUL 或换行符)结束的帧特别有用
(4)Delimiters 类为方便起见,定义了常用的定界符
(5)DelimiterBasedFrameDecoder 允许指定一个以上的定界符,如果在缓冲区中发现一个以上的定界符,它会选择产生最短帧的定界符
4、两种解码器都需要传入数据的最大长度,若超出最大长度,会抛出 TooLongFrameException 异常
LengthFieldBasedFrameDecoder
1、长度字段解码器
2、在传送数据时,可以在数据中添加一个表示有用数据长度的字段,在解码时,读取出这个表明长度的字段,同时读取其他相关参数,即可知道最终需要的数据
3、构造器
public LengthFieldBasedFrameDecoder(ByteOrder byteOrder,
int maxFrameLength,
int lengthFieldOffset,
int lengthFieldLength,
int lengthAdjustment,
int initialBytesToStrip,
boolean failFast)
(1)byteOrder:长度字段的字节顺序
(2)maxFrameLength:帧的最大长度,即数据的最大长度(包括附加信息、长度标识等内容),如果帧的长度大于这个值,将抛出 TooLongFrameException
(3)lengthFieldOffset:长度字段的起始偏移量,指明数据第几个字节开始,是用于标识有用字节长度,因为前面可能还有其他附加信息
(4)lengthFieldLength:长度字段所占字节数,即表示有用数据长度的标识所占的字节数
(5)lengthAdjustment:将补偿值添加到长度字段的值中,指明数据长度标识和有用数据之间还有多少字节,因为两者之间还可能有附加信息
(6)initialBytesToStrip:从解码帧中剥离的第一个字节的数量,数据读取起点,不读取 0 ~ initialBytesToStrip 之间的数据
(7)failFast:如果为 true,一旦解码器注意到帧的长度将超过 maxFrameLength,就会抛出一个 TooLongFrameException,而不管整个帧是否已经被读取;如果为 false,那么在读取超过 maxFrameLength 的整个帧之后,就会抛出 TooLongFrameException
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战