繁体   English   中英

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

[英]sendto() returns values greater than MTU on SOCK_DGRAM (UDP) socket

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

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

成功调用后,这些调用将返回发送的字符数。 如果出错,则返回-1,并正确设置errno。

但是,sendto始终返回其length参数,表示成功。 对于大于65507( 0xFFE3 )字节的Message too long ,它将返回Message too long错误。

对于大于1500字节MTU的消息,服务器总是(通过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。

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

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

UDP不是面向连接的,您永远不能说两个数据包是相互关联的,可以使用getsockopt()确定MTU并保持在其之下,以适当的偏移量手动准备数据包。

原始套接字的udp数据包分段对此有一些解释。

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

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

从Linux udp(7)手册页:

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

因此,这意味着任何大小大于MTU(对您来说约为1500个字节?)的sendto都应返回EMSGSIZE 如果那不是您所看到的,那么我完全不确定会发生什么。

(为什么)这种行为是故意的,是否有办法通知发生问题?

-> UDP就是这样(无连接,不可靠,数据报协议),使用UDP传输数据时需要检查每个错误

我目前能想到的唯一解决方法是简单地假设一个1500字节的MTU

->如今,99%的流量是通过以太网发送的,而在以太网上,MTU为1500字节

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

->默认情况下,Linux UDP会进行路径MTU发现。 这意味着内核将跟踪到特定目标IP地址的MTU,并在UDP数据包写入超出该值时返回EMSGSIZE。

这是Krenel内部的默认行为,您可以对其进行修改并得到碎片化的数据包(对于Performanes,这是个坏主意)。 此处的更多信息: http : //www.kernel.org/doc/man-pages/online/pages/man7/udp.7.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM