首页 > 解决方案 > 我可以在一个 UDP 数据包中发送多少数据并且仍然避免碎片?

问题描述

我有处理发送和接收 UDP 数据包的 C++ 类。到目前为止,我用那些来发送信号(PING、WAKEUP,...),换句话说,非常小的数据包,从来没有出现过问题。

现在我想发送大块数据(即0.5Mb),但为了优化丢包的可能性,我希望能够自己进行分片。首先,我编写了一个函数,它给出了 MTU 大小:

int udp_server::get_mtu_size() const
{
    if(f_mtu_size == 0)
    {
        struct ifreq ifr;
        memset(&ifr, 0, sizeof(ifr));
        strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
        if(ioctl(f_socket, SIOCGIFMTU, &ifr) == 0)
        {
            f_mtu_size = ifr.ifr_mtu;
        }
        else
        {
            f_mtu_size = -1;
        }
    }

    return f_mtu_size;
}

注意:我知道这个函数忽略的 PMTUD。如下所述,这是在受控网络上工作的,因此 MTU 路径不会仅仅改变我们。

这个函数在 Linux 下很可能返回 1500。

许多答案之间真正不清楚并且似乎相互矛盾的是,这 1,500 字节的大小不仅仅是我的有效负载。它可能包括一些我无法控制的标头(即以太网标头+页脚、IPv4 标头、UDP 标头。)

从其他一些问题和答案来看,假设我所有的 MTU 都是 1,500,我感觉我可以发送 1,500 字节的数据而不会产生碎片。

那么......这是真的吗?

  1. 我的数据缓冲区的大小可以等于MTU

  2. 我的数据缓冲区必须是MTU - sizeof(various-headers/footers)

PS 网络是我们控制 100% 的 LAN。数据包将使用 UDP 多播从一台主计算机传输到一组从属计算机。两者之间只有一个 1Gbps 交换机。而已。

标签: c++udpmtu

解决方案


The size is very clearly defined in RFC-8085: UDP Usage Guidelines.

https://www.rfc-editor.org/rfc/rfc8085#section-3.2

There is the relevant bit about the size calculation for the payload.

To determine an appropriate UDP payload size, applications MUST subtract the size of the IP header (which includes any IPv4 optional headers or IPv6 extension headers) as well as the length of the UDP header (8 bytes) from the PMTU size. This size, known as the Maximum Segment Size (MSS), can be obtained from the TCP/IP stack [RFC1122].

So in C/C++, this becomes:

#include <netinet/ip.h> // for iphdr
#include <netinet/udp.h> // for udphdr

int mss(udp.get_mtu_size());
mss -= sizeof(iphdr);
mss -= sizeof(udphdr);

WARNING: The size of the IP header varies depending on options. If you use options that will increase the size, your MSS computation must take that in account.

The size of the Ethernet header and footer are not included here because those are transparent to the UDP packet.


推荐阅读