1

我正在尝试通过 UDP 实现我自己的协议。

正如 Internet 上的许多手册所建议的那样,最好通过发送大小小于 MTU 的数据包来避免 IP 碎片。

我想知道获得最佳消息大小的最佳方法是什么?我应该以某种方式获得 MTU 值(例如,像这样),还是应该将它设置为 1300 或 1400 之类的东西,并希望它不会随着时间的推移而减少或改变?

我听说获取 MTU 值(https://en.wikipedia.org/wiki/Path_MTU_Discovery)存在一些问题,据我所知,这在很大程度上取决于当前路线和其他可能随时间变化的因素。

4

2 回答 2

2

为了在你的界面中获取 MTU,而不是为了发现路径 MTU,你有 struct ifreq。其中一个字段是 ifr_mtu,该字段将为您提供 MTU。您使用 ioctl、SIOCGIFMTU 读取此字段以获得正确的界面。(http://man7.org/linux/man-pages/man7/netdevice.7.html

struct ifreq {
   char ifr_name[IFNAMSIZ]; /* Interface name */
   union {
       struct sockaddr ifr_addr;
       struct sockaddr ifr_dstaddr;
       struct sockaddr ifr_broadaddr;
       struct sockaddr ifr_netmask;
       struct sockaddr ifr_hwaddr;
       short           ifr_flags;
       int             ifr_ifindex;
       int             ifr_metric;
       int             ifr_mtu;
       struct ifmap    ifr_map;
       char            ifr_slave[IFNAMSIZ];
       char            ifr_newname[IFNAMSIZ];
       char           *ifr_data;
   };
};

例子:

#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>


int main(void)
{
    int sock;
    char *name = "enp0s3";
    struct ifreq ifr;

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("Creating socket: %d\n", errno); 
        exit(-1);
    }

    ifr.ifr_addr.sa_family = AF_INET;
    strcpy(ifr.ifr_name, name);
    if (ioctl(sock, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
        printf("Error ioctl: %d\n", errno);
        exit(-2);
    }

    printf("MTU is %d.\n", ifr.ifr_mtu);

    close(sock);

    return 0;
}

今天,除非您使用蓝牙或 Zigbee 等特定连接技术,否则您通常可以信任 Internet 中的 MTU 为 1,500。您可以使用路径 MTU 发现并使用 ACK 实现基于 UDP 的协议,以检查对方是否已收到消息。现在,实现一个 ACK​​ 和面向连接协议的特性与使用 TCP 是不一样的。如果你可以用 UDP 做任何事情,它比 TCP 更轻。

编辑:对于使用路径 MTU 发现,您还可以使用带有选项 IP_PMTUDISC_DO 的 getsockopt:

IP_MTU_DISCOVER (since Linux 2.2)
              Set or receive the Path MTU Discovery setting for a socket.
              When enabled, Linux will perform Path MTU Discovery as defined
              in RFC 1191 on SOCK_STREAM sockets.  For non-SOCK_STREAM
              sockets, IP_PMTUDISC_DO forces the don't-fragment flag to be
              set on all outgoing packets.  It is the user's responsibility
              to packetize the data in MTU-sized chunks and to do the
              retransmits if necessary.  The kernel will reject (with
              EMSGSIZE) datagrams that are bigger than the known path MTU.
              IP_PMTUDISC_WANT will fragment a datagram if needed according
              to the path MTU, or will set the don't-fragment flag
              otherwise.

在此处查看更多信息:http: //man7.org/linux/man-pages/man7/ip.7.html

于 2016-08-07T18:36:41.127 回答
2

IPv4 UDP 的推荐大小为 576 个八位字节。每个 Internet 路由器都应该保证至少具有该大小的 IPv4 MTU,并且由于 UDP 是一种无连接、即发即弃、尽力而为、无保证的传输协议,因此每个数据包可能会带来更少的数据风险丢失,并且会有丢失的数据包。

IPv6 的最低 MTU 要求为 1280 个八位字节,并且路径中没有分段。

于 2016-08-07T18:16:06.497 回答