我如何在 C++ 中以编程方式确定 MTU [英] How do i programmatically determine MTU, in C++

查看:226
本文介绍了我如何在 C++ 中以编程方式确定 MTU的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个应用程序,我需要将多个 UDP 数据包中的大数据发送到客户端,如何以编程方式确定我的 UDP 套接字的 MTU?

I am working on an application where I need to send large data in multiple UDP packets to a client, how can I determine programmatically the MTU for my UDP socket?

我需要能够在 windows 和 linux 上执行此操作.

I need to be able to do this on both windows and linux.

推荐答案

这是我的 eventdispatcher 库中的两个函数(查看udp_base.cpp 了解更多详情).

Here are my two functions from my eventdispatcher library (look at the udp_base.cpp for more details).

第一个返回实际的 MTU 大小.通常1500用于计算机间通信.在 locahost 上它可以更大(大约 64K).

The first one returns the actual MTU size. Often 1500 for inter-computer communication. It can be much larger on locahost (about 64K).

MTU 大小的问题在于它包含了您以外的数据.换句话说,您可用的大小不是 MTU.相反,您必须找出 MSS 是什么.这是下面的第二个函数.

The problem of the MTU size is that it includes data other than yours. In other words, the size available to you is not the MTU. Instead you have to find out what the MSS is. That's the second function below.

到目前为止一切都很好,它可以在 Linux 下运行,我想 MS-Windows 可能有类似的调用,只是 API 不同.

So far so good, it works under Linux, I would imagine that MS-Windows may have similar calls, just a different API.

/** \brief Retrieve the size of the MTU on that connection.
 *
 * Linux offers a ioctl() function to retrieve the MTU's size. This
 * function uses that and returns the result. If the call fails,
 * then the function returns -1.
 *
 * The function returns the MTU's size of the socket on this side.
 * If you want to communicate effectively with another system, you
 * want to also ask about the MTU on the other side of the socket.
 *
 * \note
 * MTU stands for Maximum Transmission Unit.
 *
 * \note
 * PMTUD stands for Path Maximum Transmission Unit Discovery.
 *
 * \note
 * PLPMTU stands for Packetization Layer Path Maximum Transmission Unit
 * Discovery.
 *
 * \todo
 * We need to support the possibly dynamically changing MTU size
 * that the Internet may generate (or even a LAN if you let people
 * tweak their MTU "randomly".) This is done by preventing
 * defragmentation (see IP_NODEFRAG in `man 7 ip`) and also by
 * asking for MTU size discovery (IP_MTU_DISCOVER). The size
 * discovery changes over time as devices on the MTU path (the
 * route taken by the packets) changes over time. The idea is
 * to find the smallest MTU size of the MTU path and use that
 * to send packets of that size at the most. Note that packets
 * are otherwise automatically broken in smaller chunks and
 * rebuilt on the other side, but that is not efficient if you
 * expect to lose quite a few packets. The limit for chunked
 * packets is a little under 64Kb.
 *
 * \note
 * errno is either EBADF or set by ioctl().
 *
 * \sa
 * See `man 7 netdevice`
 *
 * \return -1 if the MTU could not be retrieved, the MTU's size otherwise.
 */
int udp_base::get_mtu_size() const
{
    if(f_socket != nullptr
    && f_mtu_size == 0)
    {
        addr::addr a;
        switch(f_addrinfo->ai_family)
        {
        case AF_INET:
            a.set_ipv4(*reinterpret_cast<struct sockaddr_in *>(f_addrinfo->ai_addr));
            break;

        case AF_INET6:
            a.set_ipv6(*reinterpret_cast<struct sockaddr_in6 *>(f_addrinfo->ai_addr));
            break;

        default:
            f_mtu_size = -1;
            errno = EBADF;
            break;

        }
        if(f_mtu_size == 0)
        {
            std::string iface_name;
            addr::iface::pointer_t i(find_addr_interface(a));
            if(i != nullptr)
            {
                iface_name = i->get_name();
            }

            if(iface_name.empty())
            {
                f_mtu_size = -1;
                errno = EBADF;
            }
            else
            {
                ifreq ifr;
                memset(&ifr, 0, sizeof(ifr));
                strncpy(ifr.ifr_name, iface_name.c_str(), sizeof(ifr.ifr_name));
                if(ioctl(f_socket.get(), SIOCGIFMTU, &ifr) == 0)
                {
                    f_mtu_size = ifr.ifr_mtu;
                }
                else
                {
                    f_mtu_size = -1;
                    // errno -- defined by ioctl()
                }
            }
        }
    }

    return f_mtu_size;
}


/** \brief Determine the size of the data buffer we can use.
 *
 * This function gets the MTU of the connection (i.e. not the PMTUD
 * or PLPMTUD yet...) and subtract the space necessary for the IP and
 * UDP headers. This is called the Maximum Segment Size (MSS).
 *
 * \todo
 * If the IP address (in f_addr) is an IPv6, then we need to switch to
 * the corresponding IPv6 subtractions.
 *
 * \todo
 * Look into the the IP options because some options add to the size
 * of the IP header. It's incredible that we have to take care of that
 * on our end!
 *
 * \todo
 * For congetion control, read more as described on ietf.org:
 * https://tools.ietf.org/html/rfc8085
 *
 * \todo
 * The sizes that will always work (as long as all the components of the
 * path are working as per the UDP RFC) are (1) for IPv4, 576 bytes, and
 * (2) for IPv6, 1280 bytes. This size is called EMTU_S which stands for
 * "Effective Maximum Transmission Unit for Sending."
 *
 * \return The size of the MMU, which is the MTU minus IP and UDP headers.
 */
int udp_base::get_mss_size() const
{
    // where these structures are defined
    //
    // ether_header -- /usr/include/net/ethernet.h
    // iphdr -- /usr/include/netinet/ip.h
    // udphdr -- /usr/include/netinet/udp.h
    //
    int const mtu(get_mtu_size()
            //- sizeof(ether_header)    // WARNING: this is for IPv4 only -- this is "transparent" to the MTU (i.e. it wraps the 1,500 bytes)
            //- ETHER_CRC_LEN           // this is the CRC for the ethernet which appears at the end of the packet
            - sizeof(iphdr)             // WARNING: this is for IPv4 only
            //- ...                     // the IP protocol accepts options!
            - sizeof(udphdr)
        );

    return mtu <= 0 ? -1 : mtu;
}

这篇关于我如何在 C++ 中以编程方式确定 MTU的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆