在学习使用久久派摄像头的时候,我注意到我代码中出现了一个限制数据包段长度的一个代码,我对此感到很疑惑,然后便有了如下的解答
为什么要限制?
UDP段长度为1500字节时,常见的MTU限制导致的分片问题如何解决?_编程语言-CSDN问答
https://ask.csdn.net/questions/8195305
原因概述:UDP分片与MTU限制
在使用UDP协议传输数据时,当UDP段长度达到或超过1500字节(典型以太网MTU值),可能会引发IP层分片。分片会增加网络延迟、带宽消耗以及丢包风险,从而影响性能。
MTU是传输链路允许的最大帧大小,而且每个udp 的头部也会有八个字节的开支,一旦超过MTU的最大帧,IP层会将数据拆散为许多较小的数据包。但要注意"分片" 仅针对 "单个超大数据包",每次发送的UDP数据是不同 IP 数据报,不会复用之前的分片 "剩余空间"。
例如,在一个标准以太网环境中:
字段 大小(字节)
最大帧 1500
以太网头部 14
IP头部 20
UDP头部 8
用户数据 1472
因此我们要对数据帧进行限制,这是最简单的方法
第二种是采用PMTUD(路径MTU发现),它会探测路径上最小的MTU,动态调整数据包大小
第三种是使用GSO/GRO,也就是硬件加速。
关于数据分片的相关解释
当数据包超过MTU时,IP层会将其拆分为多个较小的数据包。如果没有超出,那么下一次到来是在上一次数据的后面剩余片里面继续写还是重新开一个新片?
这个其实上文黑字已经进行解答了,但是我第一遍没有看见,又去搜索一下了。
一、(一句话说清)
未超过 MTU:无分片,每次发送都是全新的独立 IP 数据报,不存在 "续写剩余片";
超过 MTU:仅单个超大数据包会拆分成多个分片(同属一个数据报),下一次新数据无论是否超 MTU,都重新开新的 IP 数据报(或新的分片组),不续写之前的分片。
本质是:"分片" 是对 "单个超 MTU 数据包" 的拆分逻辑,而 "多次 UDP 发送" 是多个独立的数据报传输,彼此无分片层面的关联。
下面是详细的解释,想看就看, 已经核心是先明确 "分片仅发生在'单个 UDP 数据包超过 MTU'时" ------ 如果数据包未超过 MTU,根本不会产生 "分片",自然不存在 "在上一次剩余片里续写" 的情况,每次发送都是独立的完整数据报。具体分两种场景详细说明:
二、核心前提:先区分 "未分片" 和 "分片" 的本质
分片的触发条件:只有当 单个 UDP 数据包(含 IP 头 + UDP 头 + 用户数据)的总长度 > 路径 MTU 时,IP 层才会将这个 "超大数据包" 拆分成多个 "IP 分片"(这些分片属于同一个原始 IP 数据报,共享同一个 IP 标识字段)。
未分片的情况:如果单个 UDP 数据包总长度 ≤ MTU,IP 层不会拆分,直接封装成一个完整的 IP 数据报(无分片),整个数据包作为独立单元传输。
三、分两种场景说明
场景 1:数据包未超过 MTU(无分片)→ 每次都是 "重新开一个新的完整数据报"
发送的 UDP 数据未超过 MTU 时,不存在 "片" 的概念(因为没有被拆分),每次发送的逻辑是:
应用层每次调用 UDP 发送(比如sendto),都会将当前数据封装成一个 独立的 IP 数据报(完整,不分片);
下一次到来的新数据,会重复这个过程zhi:封装成全新的、独立的 IP 数据报,和上一次发送的完整数据报没有任何 "续写" 关系 ------ 因为上一次没有分片,也就没有 "剩余片" 可以续写。
简单说:未超 MTU 时,"每次发送 = 一个独立完整的 IP 数据报",没有 "片" 的拆分与续写,彼此完全独立。
场景 2:数据包超过 MTU(有分片)→ 仅对 "同一个超大数据包" 拆分,下一次新数据仍为新数据报
当单个 UDP 数据包超 MTU 时,IP 层会将其拆分成多个 "IP 分片"(比如拆成 2 片、3 片),这些分片属于 同一个原始 IP 数据报(通过 IP 头的 "标识" 字段关联);
接收端会收集所有分片,根据 "标识" 重组为完整的原始数据报,再交给 UDP 层;
但 下一次新发送的 UDP 数据(无论是否超 MTU),都会封装成 新的 IP 数据报(分配新的 IP 标识):
若新数据未超 MTU:新的完整数据报(无分片);
若新数据超 MTU:新的 IP 数据报被拆分成新的分片(和上一次的分片无关联)。
这里关键是:"分片" 仅针对 "单个超大数据包",不同 UDP 发送的是不同 IP 数据报,不会复用之前的分片 "剩余空间"。
我们要传输的图片无论怎么都会超过片区大小,限制大小还有什么用?
我们不试图让 "整张图片" 适配 MTU,而是让 "传输图片的每个小数据包" 适配 MTU,限制 BUF_SIZE 的价值就体现在这里。
把整张图片比作「一箱 10 公斤的货物」,把 MTU 比作「快递站的单个包裹限重 1.5 公斤」,把 BUF_SIZE 比作「你打包的单个包裹重量 1.4 公斤」:
不限制 BUF_SIZE:直接把 10 公斤货物装成一个包裹,快递站(IP 层)会强制把它拆分成 7 个小包裹(分片)运输,一旦丢了其中 1 个,整个 10 公斤货物都无法完整签收(其余 6 个也没用);
限制 BUF_SIZE=1.4 公斤:你主动把 10 公斤货物拆分成 8 个 1.4 公斤的小包裹(应用层分包),每个包裹都符合快递站限重,即使丢了 1 个,你还能收到其余 7 个,甚至可以让快递员补寄丢失的那个包裹。(UDP其实没有重发选项)
总之就是我们提前将数据分成统一规格的小份,这样就是我们就能知道我们整个文件是被怎么拆分的了,以及可以规范每片大小。(虽然在我写这篇文章的时候我感觉,这个限制其实并没有什么用,因为我们都已经选择使用udp了,说明我们已经更加追求时间而不是传输的质量,这个分块交给底层代码去自动分块也未尝不可。)