Netty学习
🗒️Netty之进阶
00 min
2022-12-1
2023-7-31
type
status
date
slug
summary
tags
category
icon
password

一、粘包与半包

粘包和半包在网络通信中是一种常见的现象。粘包主要在数据传输时,服务端在一条信息中读取到另外一条信息的数据。半包指的是服务端只接收到部分数据,而非完整的数据。主要是由于TCP是面向连接、以“流”的形式传输数据的协议,而“流”数据是没有明确的开始和结尾边界

1.1、粘包

客户端发送两次消息分别为abc、def,而服务端只接收到一次消息为abcdef。
原因:
  • 应用层:接收方 ByteBuf 设置太大(Netty默认1024)
  • 滑动窗口:假设发送方256bytes表示一个完整报文,但由于接收方处理不及时且窗口大小足够大,这256bytes字节就会缓冲在接收方的滑动窗口中,当滑动窗口缓冲了多个报文就会粘包。
  • Nagle算法:会造成粘包。

1.2、半包(拆包)

客户端发送了一次消息abcdef,而服务端却接收到两次消息abc、def,将原本的完整消息拆分了。
原因:
  • 应用层:接收发 ByteBuf 小于实际发送数据量
  • 滑动窗口:假设接收方的窗口只剩了128bytes,发送方的报文大小是256bytes,这是放不下,只能先发送128bytes,等待ack后才能发送剩余部分,这就造成了半包。
  • MSS限制:当发送的数据超过MSS限制后,会将数据切分发送,就会造成了半包。
本质是TCP是面向连接、以流的形式传输数据的协议,消息无边界。

1.3、滑动窗口

TCP以一个段(segment)为单位,每发送一个段就需要进行一次确认应答(ack)处理,但如果这么做,缺点是包的往返时间越长性能就越差
notion image
为了解决性能差的问题,就引入了窗口概念,窗口大小即决定了无需等待应答而可以继续发送的数据最大值。
notion image
窗口实际就起到一个缓冲区的作用,同时也能起到流量控制的作用
  • 图中深色的部分即要发送的数据,高亮的部分即窗口
  • 窗口内的数据才允许被发送,当应答未到达前,窗口必须停止滑动
  • 如果 1001~2000 这个段的数据 ack 回来了,窗口就可以向前滑动
  • 接收方也会维护一个窗口,只有落在窗口内的数据才能允许接收

1.4、MSS限制

  • 链路层对一次能够发送的最大数据有限制,这个限制称之为 MTU(maximum transmission unit),不同的链路设备的 MTU 值也有所不同,例如
  • 以太网的 MTU 是 1500
  • FDDI(光纤分布式数据接口)的 MTU 是 4352
  • 本地回环地址的 MTU 是 65535 - 本地测试不走网卡
  • MSS 是最大段长度(maximum segment size),它是 MTU 刨去 tcp 头和 ip 头后剩余能够作为数据传输的字节数
  • ipv4 tcp 头占用 20 bytes,ip 头占用 20 bytes,因此以太网 MSS 的值为 1500 - 40 = 1460
  • TCP 在传递大量数据时,会按照 MSS 大小将数据进行分割发送
  • MSS 的值在三次握手时通知对方自己 MSS 的值,然后在两者之间选择一个小值作为 MSS
notion image

1.5、Nagle算法

  • 即使发送一个字节,也需要加入 tcp 头和 ip 头,也就是总字节数会使用 41 bytes,非常不经济。因此为了提高网络利用率,tcp 希望尽可能发送足够大的数据,这就是 Nagle 算法产生的缘由
  • 该算法是指发送端即使还有应该发送的数据,但如果这部分数据很少的话,则进行延迟发送
    • 如果 SO_SNDBUF 的数据达到 MSS,则需要发送
    • 如果 SO_SNDBUF 中含有 FIN(表示需要连接关闭)这时将剩余数据发送,再关闭
    • 如果 TCP_NODELAY = true,则需要发送
    • 已发送的数据都收到 ack 时,则需要发送
    • 上述条件不满足,但发生超时(一般为 200ms)则需要发送
    • 除上述情况,延迟发送

1.6、解决方案

  1. 短链接,发一个包建立一次连接,这一连接建立到连接断开之间就是消息的边界,但是效率太低,需要重复的连接与断开,十分影响性能。
  1. 固定长度,每一条消息采用一定的长度,缺点浪费空间。
  1. 采用分隔符,每一条消息采用分隔符,例如\n结尾,缺点是需要转义。
  1. 预设长度,每一条消息分为head和body,head中包含body的长度。

1、短链接

将消息发送完就关闭连接通道channel

2、固定长度

让所有数据包长度固定(假设长度为8字节),服务端childHandler中加入
客户端中
缺点:数据包的长度大小不好把握
  • 长度定的太大,浪费资源。
  • 长度定的太小,可能不满足某些数据包的长度,会造成半包现象。

3、固定分隔符

服务端加入,默认以\n或\r\n作为分隔符,如果超出指定长度仍未出现分隔符,则抛出异常
客户端在每条消息最后加入\n分隔符
缺点
  • 处理字符数据比较合适,但如果内容本身包含了分隔符(字节数据常常会有此情况),那么就会解析错误

4、预设长度

利用LengthFieldBasedFrameDecoder(),

二、协议设计与解析

上一篇
Netty之入门
下一篇
环境配置All in One