粘包、半包

粘包原因

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 

posted @   半条咸鱼  阅读(157)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示