我正在编写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。
答案 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