sendto()在SOCK_DGRAM(UDP)套接字上返回大于MTU的值

时间:2012-08-04 15:21:56

标签: c linux sockets udp mtu

我正在编写UDP客户端/服务器(在GNU / Linux上)。 我正在使用sendto()在尚未绑定到端口的SOCK_DGRAM套接字上发送消息。

send(2)联机帮助页指出:

  

成功时,这些调用将返回发送的字符数。上   错误,返回-1,并正确设置errno。

但是,sendto始终返回其长度参数,表示成功。 如果消息大于65507(0xFFE3)个字节,则会返回Message too long错误。

对于大于MTU为1500字节的消息,服务器始终接收(通过recvfrom())恰好1500字节的消息,只需剪切消息,恕不另行通知。

(为什么)这种行为是有意的,有没有办法通知出现问题?

我目前唯一能想到的解决方法就是假设一个1500字节的MTU并且永远不会发送更大的数据包。

这是相关的方法:

int udp_send(uint32_t dst, uint16_t port, char *msg, unsigned len) {
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0) {
            perror("Could not open socket");
            return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(dst);

    int count = sendto(sock, msg, len, 0,
            (struct sockaddr *) &addr, sizeof(addr));
    printf("bytes sent: %d\n", count);

    if(count < 0) {
            perror("Could not send message");
            return -3;
    }

    close(sock);
    return count;
}

发生所描述的不期望行为的呼叫将是 udp_send(0x7F000001,1337,bigbuf,1501); 只发送1500个字节时返回1501。

4 个答案:

答案 0 :(得分:3)

事实证明,正如nos在他的评论中所建议的那样,我的发送代码实际上非常好;我的接收代码有一个简单的错误:在某些时候,我将其缓冲区定义为大小只有1500字节,稍后就忘记了。

此外,我误以为GNU / Linux只能将UDP数据包发送到接口的MTU,这是不正确的。即使标准只保证576字节,至少在我的情况下,似乎可以发送(和接收)最多65507字节的UDP数据包,之后Message too long调用返回sendto()错误。 UDP数据包由内核的IP层自动分段,并由接收器重新组装 - 我不认为UDP能够使用这种高级技术。

答案 1 :(得分:2)

UDP不是面向连接的,你永远不能说两个数据包彼此属于你,你可以使用getsockopt()来确定MTU并保持在它之下,用适当的偏移手动准备数据包。

udp packet fragmentation for raw sockets对此有一些解释。

另一方面,您可以尝试使用MTU发现。基本上,当发送大于MTU的UDP数据包时,如果您的平台支持IP分段,但并非所有平台都支持,则会发生IP分段。中间的一些网络设备会丢弃碎片包,有些会通过它们,你无法保证在交付时重新组装。

以下是一些背景信息:http://michael.toren.net/mirrors/sock-faq/unix-socket-faq-5.html

答案 2 :(得分:1)

从Linux udp(7)手册页:

  

默认情况下,Linux UDP执行路径MTU(最大传输单元)发现。这意味着内核将跟踪特定的MTU          目标IP地址,并在UDP数据包写入超过时返回EMSGSIZE          它。发生这种情况时,应用程序应该减少数据包          尺寸。也可以使用IP_MTU_DISCOVER套接字选项或/ proc / sys / net / ipv4 / ip_no_pmtu_disc文件关闭路径MTU发现;看到          ip(7)了解详情。关闭时,UDP将分段传出的UDP          超过接口MTU的数据包。但是,禁用它不是          建议出于性能和可靠性的原因。

所以这意味着任何大小都超过MTU的sendto(大约1500字节?)应该返回EMSGSIZE。如果那不是你所看到的,我完全不确定会发生什么。

答案 3 :(得分:0)

(为什么)这种行为是有意的,有没有办法通知出现问题?

- &GT; UDP就像那样(无连接,不可靠,数据报协议),你需要在使用UDP转发数据时检查每个错误

  

我目前唯一能想到的解决方法就是假设MTU为1500字节

- &GT;在我们这个时代,99%的流量通过以太网发送,而在以太网上,MTU是1500字节

  

发生描述的不良行为的调用将是udp_send(0x7F000001,1337,bigbuf,1501);只发送1500个字节时返回1501。

- &GT;默认情况下,Linux UDP执行路径MTU发现。这意味着内核将跟踪特定目标IP地址的MTU,并在UDP数据包写入超过它时返回EMSGSIZE。

这是krenel内部的默认行为,你可以修改它并得到格式化的数据包(对于performanes来说这是一个坏主意)。 更多相关内容:http://www.kernel.org/doc/man-pages/online/pages/man7/udp.7.html