4

作用于 UDP 套接字时,什么会导致sendto()发送的字节数少于请求的字节数?

问这个问题的动机是弄清楚我需要采取的预防措施,以确保我总是在一次调用中收到完整的消息sendto(),并了解我需要采取哪些进一步的步骤才能将消息放入单个 IP 数据包中。我是否只需要确保我的消息小于某个大小,如果是,该大小有多大?除了操作系统特定的 UDP 数据报大小限制和 MTU 之外,是否还有其他因素在起作用(例如 i/o 缓冲区容量、反复无常的操作系统)?

在问了上面的原则问题之后,在这篇文章的标题中,我将继续一些相关的后续问题,然后在最后把事情放到上下文中。

进一步的问题

更详细地说,再次假设我们正在对 UDP 套接字进行操作:

  1. 每次成功调用是否都会sendto()发送1 个UDP 数据报?(我很欣赏这可能会分成多个 IP 数据包)

  2. 每次成功调用都会recvfrom()准确检索1 个UDP 数据报吗?

  3. 如果一条消息需要N次调用才能发送,即使接收机器是不同的平台sendto(),它是否也需要恰好N次调用才能接收?recvfrom()(我很欣赏数据报的顺序将是不可预测的)

  4. 假设我尝试发送一条消息,其大小等于或小于本地和远程系统支持的最大 UDP 数据报大小中的较小者,(并且出现一些会导致返回值-1的错误)sendto()保证一口气发送我的全部信息?或者它可能会报告它发送的字节数少于我要求它发送的字节数?如果是这样,为什么?回到问题 1。

  5. 除了问题 4 中的假设之外,假设我的消息不大于(MTU - UDP 标头 - IP 标头)大小,是保证适合1 个IP 数据包的 UDP 数据报(至少在我的本地网络上) ?

语境

我刚刚开始编写我的第一个基于 UDP 的通信协议(跨平台:例如 linux、mac、windows、ios、android 等)。我是一个套接字新手,但我知道使用像 UDP 这样简单的协议所带来的“成本”,并研究了以下算法/策略:

  • 可靠性和流量控制
  • 将大消息拆分为多个小消息
  • 喷泉码有助于补偿一些数据包丢失(通过减少重新发送请求)。
  • 加密(见 http://srp.stanford.edu/)

我试图将我的所有通信分解为原子消息(即单个、自包含的 UDP 数据报),这些消息可能(但不一定)需要适合单个 IP 数据包(例如 1500 字节)。吞吐量和数据包丢失的实时评估将确定我是否必须缩小数据报以适应单个 IP 数据包(这会导致额外标头的大小损失)。其中一些将通过 wifi/无线电链接,所以我希望自适应地确定“最佳”数据报大小。我知道我所有接口的 MTU,并且意识到在我的本地网络之外,数据包可能会被进一步拆分,但这超出了我的控制范围,所以我可以忍受它。

但是一切都取决于能够构造一个原子消息,并且有 100% 的信心我可以通过一次调用成功发送它sendto(),并通过一次调用接收 if recvfrom()sendto()我所有的应用程序级可靠性、拆分、编码和加密信息都存在于我自己的协议头中,并且在调用返回短消息后我无法重新拆分消息。例如,考虑一个消息校验和:如果整个消息没有一次性通过,则标头中的校验和对于已发送的消息部分不再有效。

4

2 回答 2

3

一切都取决于能够构造一个原子消息,并且有 100% 的信心我可以通过一次调用 sendto() 成功发送它,并通过一次调用 recvfrom() 接收 if

UDP 保证了这一点。数据报完好无损地到达,或者根本不到达。您所需要的只是确保您的套接字发送和接收缓冲区足够大,并且如果您正在遍历路由器,则每个数据报发送的字节数不要超过 534 个字节:这是普遍接受的限制。

于 2014-06-04T22:11:04.227 回答
0

我有许多协议的经验,这些协议做出了这个确切的基本假设,即发送的任何 UDP 数据包都将在一个调用中发送并在 1 个调用中接收(或根本不接收)。由于 MTU 等而发生的任何碎片在应用程序级别都看不到。许多实时流协议限制数据包大小以限制由于碎片导致的延迟,但这一切都在幕后。只要确保接收缓冲区足够大。

于 2014-06-04T19:32:09.493 回答