TCP/UDP 和以太网 MTU 碎片
我在线阅读了各种网站和教程,但仍然感到困惑。如果消息大于 IP MTU,则 send()
返回发送的字节。消息的其余部分会发生什么情况?我是否需要再次调用 send()
并尝试发送消息的其余部分?或者 IP 层应该自动处理这些事情?
I've read various sites and tutorials online but I am still confused. If the message is bigger than IP MTU, then send()
returns the byte sent. What happens to the rest of the message? Am I to call send()
again and attempt to send rest of the message? Or is that something IP layer should take care of automatically?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您使用 TCP,那么呈现给您的接口就是字节流的接口。您无需担心字节流如何从连接的一端到达另一端。您可以忽略IP层的MTU。事实上您可以完全忽略IP 层。
当您调用
send()
时,计算机上的 TCP 堆栈将处理您推送到 send 调用中的字节流所需的所有详细信息,以便从recv()
中显示code> 在连接的另一端调用。要记住的一件事是,对于 TCP,您正在处理一个流,这意味着一个
send()
可能会导致数据通过多个recv()
调用到达,并且多个send()
调用可能会导致数据在单个recv()
调用中到达。你对此无法控制。您正在处理字节流,每次调用recv()
都可以返回从 1 到当前未完成的数字的任意数量的字节(允许将足够的缓冲区传递给recv()< /code> 调用)。
因为评论者要求它;)
在大多数 TCP 堆栈上,
send()
很可能无法发送所有内容,因为 TCP 堆栈的缓冲区已满,并且(可能)TCP 窗口也已满并且流量控制正在运行,这意味着堆栈无法发送更多数据,直到远程端确认一些数据并且它不准备代表您再缓冲。我还没有遇到过仅由于 MTU 考虑而拒绝send()
的 TCP 堆栈,但我猜某些精简的嵌入式系统可能会这样做......无论如何,如果
send ()
返回的字节数小于您提供的字节数,那么您应该在某个时候重新发送剩余的数据。通常send()
会阻塞并等待,直到它可以发送所有数据,如果您已将套接字设置为非阻塞模式,那么您可能不想在发送失败时立即重试发送发送所有内容,因为您可能最终会陷入紧密循环...更具体地了解您正在使用的操作系统可能会对您很有用。
If you are using TCP then the interface presented to you is that of a stream of bytes. You don't need to worry about how the stream of bytes gets from one end of the connection to the other. You can ignore the IP layer's MTU. In fact you can ignore the IP layer entirely.
When you call
send()
the TCP stack on your machine will deal with all the details necessary for the stream of bytes that you are pushing into your send calls to appear fromrecv()
calls at the other end of the connection.The one thing to remember is that with TCP you are dealing with a stream and that means that one
send()
may result in data arriving in multiplerecv()
calls and multiplesend()
calls may result in data arriving in a singlerecv()
call. You have no control over this. You are dealing with a stream of bytes and each call torecv()
can return any number of bytes from 1 to the number currently outstanding (allowing for adequate buffers passed to therecv()
call).Since the commenters asked for it ;)
On most TCP stacks
send()
is most likely to fail to send everything because the TCP stack's buffers are full and (probably) the TCP window is also full and flow control is in operation which means that the stack can't send any more data until the remote end ACKs some data and it's not prepared to buffer any more on your behalf. I've not come across a TCP stack that will refuse asend()
due to MTU considerations alone but I guess some slimmed down embedded systems might behave that way...Anyway, if
send()
returns less than the number of bytes that you supplied it then you should resend the remaining data at some point. Oftensend()
will block and wait until it can send all of the data, and if you've set the socket into non blocking mode then you probably do NOT want to immediately retry the send if it fails to send everything as you'll likely end up in a tight loop...It would probably be useful for you to be more specific about the operating system that you're using.
如果数据包太大而无法通过网络,则会发送 ICMP 分段提示,通知发送方减小数据包大小并重试。
如果您使用 TCP,这些都是您应该期望网络层为您处理的细节。现代 IP 堆栈在幕后实际执行的操作来计算路径上的最低 MTU 似乎已成为一种魔法。
WRT UDP,您仍然可以期望堆栈为您分段,但实际上考虑到 UDP 的用例,它并不理想。根据您的应用程序,通过明确了解路径 MTU,您可能会看到更好的性能。
...关于 send() 问题,某些堆栈的行为不同,但处理 WRT 您的代码应该是相同的。假设您有 100 个字节要发送...send() 返回已发送的 10 个字节。您需要继续使用剩余的 90 个字节调用 send,直到将其全部推出线路以发送整个消息。
在 Windows 平台上使用阻塞套接字 send() 通常会在发送完所有内容后返回。在其他平台上。Linux 等您将需要更频繁地发送数据来推送数据。
If the packet is too large to transit the network an ICMP fragmentation hint is sent signaling the sender to reduce the packet size and try again.
If you use TCP these are all details you should expect the network layer to take care of for you. What modern IP stacks actually do behind the scenes to figure out the lowest MTU along the path seems to have become somewhat of a black art.
WRT UDP you can still expect the stack to fragment for you but practically given the use case for UDP its not ideal.. depending on your application you are likely to see better performance by explicitly understanding the path MTU.
... on the send() question some stacks behave differently but the treatment WRT your code should be the same. Lets say you have 100 bytes to send... send() returns 10 bytes sent. You need to keep calling send with the remaining 90 bytes until its all pushed out the wire to send the entire message.
Using blocking sockets on the windows platform send() will ususally return after everything is sent.. On other platforms.. Linux et al you will need to keep sending more often to push the data.