TCP/UDP 和以太网 MTU 分片

Posted

技术标签:

【中文标题】TCP/UDP 和以太网 MTU 分片【英文标题】:TCP/UDP and ethernet MTU Fragmentation 【发布时间】:2011-01-25 07:58:46 【问题描述】:

我已经在线阅读了各种网站和教程,但我仍然感到困惑。如果消息大于 IP MTU,则send() 返回发送的字节。消息的其余部分会发生什么?我是否要再次致电send() 并尝试发送其余消息?还是 IP 层应该自动处理的事情?

【问题讨论】:

【参考方案1】:

如果您使用 TCP,那么呈现给您的接口就是字节流接口。您无需担心字节流如何从连接的一端到达另一端。您可以忽略 IP 层的 MTU。事实上,您可以完全忽略 IP 层。

当您调用send() 时,您计算机上的 TCP 堆栈将处理所有必要的细节,以便您推送到发送调用中的字节流从连接另一端的recv() 调用中出现。

要记住的一件事是,使用 TCP 您正在处理一个流,这意味着一个 send() 可能导致数据到达多个 recv() 调用,而多个 send() 调用可能导致数据到达一个单个recv() 电话。你无法控制这一点。您正在处理字节流,每次调用 recv() 都可以返回从 1 到当前未完成的字节数之间的任意字节数(允许将足够的缓冲区传递给 recv() 调用)。

因为评论者要求它;)

在大多数 TCP 堆栈上,send() 最有可能无法发送所有内容,因为 TCP 堆栈的缓冲区已满,并且(可能)TCP 窗口也已满并且流控制正在运行,这意味着堆栈无法发送任何更多数据,直到远程端确认一些数据并且它不准备代表您再缓冲。我还没有遇到一个 TCP 堆栈会因为 MTU 的考虑而拒绝 send(),但我猜一些精简的嵌入式系统可能会这样......

无论如何,如果send() 返回的字节数少于您提供的字节数,那么您应该在某个时候重新发送剩余的数据。 send() 通常会阻塞并等待它可以发送所有数据,如果您已将套接字设置为非阻塞模式,那么如果它无法发送所有数据,您可能不想立即重试发送。可能会陷入一个紧密的循环......

更具体地了解您正在使用的操作系统可能对您有用。

【讨论】:

+1,但为了完成,您还应该处理问题最感兴趣的问题:如果 send 返回的值小于要发送的请求字节(极不可能的情况,因为堆栈会尝试处理它)如果必须,您有责任稍后尝试发送其余数据。 真的不太可能吗?因为当请求的字节超过 MTU 时,send() 返回的值似乎小于要发送的请求字节。 c 中的 Unix。但是你已经详细地回答了我的问题。非常感谢!!【参考方案2】:

如果数据包太大而无法通过网络,则会发送 ICMP 分段提示,通知发送方减小数据包大小并重试。

如果您使用 TCP,这些都是您应该期望网络层为​​您处理的所有细节。现代 IP 堆栈实际上在幕后所做的以找出沿途最低 MTU 的做法似乎已成为某种黑色艺术。

WRT UDP 您仍然可以期望堆栈为您分段,但实际上考虑到 UDP 的用例并不理想。根据您的应用程序,您可能会通过明确了解路径 MTU 看到更好的性能。

... 在 send() 问题上,一些堆栈的行为不同,但处理 WRT 您的代码应该是相同的。假设您有 100 个字节要发送... send() 返回发送的 10 个字节。您需要使用剩余的 90 个字节继续调用 send,直到它全部推出来发送整个消息。

在 Windows 平台上使用阻塞套接字 send() 通常会在发送完所有内容后返回。在其他平台上。Linux 等您将需要更频繁地发送数据以推送数据。

【讨论】:

以上是关于TCP/UDP 和以太网 MTU 分片的主要内容,如果未能解决你的问题,请参考以下文章

TCP 、UDP包的最大长度是多少?

tcp分片和ip分片的区别

TCP 的断包和粘包

分片

IP分片(IP Fragment)

IP分片/TCP分段/正反向代理