重新排序在多个链接上发送的字节流段的想法

Ideas for reordering segments of a bytestream that were sent on multiple links

短版:

我正在尝试对可能代表任何内容的字节流(例如 TCP 流)进行分段,然后通过多个(不可靠的)link 将它们发送到我控制下的接收系统。在那里,我想将这些段重新排序为发送系统首先收到它们的顺序。有关示例,请参见第二个 ascii 图像。生成的隧道不需要像 TCP 一样可靠(UDP-ish 很好),但它应该处理使用多个内部 link 的事实(这不是微不足道的)。

使用包含 sizesequence number 字段的 header 可以解决所有问题,但仅限于完美世界。如果 link 中的一个行为不当并且对等点不同步(意思是,接收方将读取它认为应该构成 header 但没有读取的字节),则需要某种重新同步机制。关于 UDP,我想主要的网络堆栈会解析完整的 UDP header 并对它是否不同步做出有根据的猜测,但在此类失败后是否有重新回到正轨的好方法?

更长的版本,如果需要更多 details/context 则使用:

我正在尝试在几个不同的 links/tunnels 之上创建一个隧道。这些 link 可能不可靠,它们的设置方式无关紧要:套接字、tun、tap,你有什么。我正在创建的隧道不在乎。显然,它真正关心的是它的客户获得良好的服务,类似于您对 UDP 的期望。

当前版本:

我已经在一个可靠的 link 上实现了这样一个工具作为第一个版本(简单地说,我将 here 引用的源代码移植到 C++):

 +------\     TCP     /------+
tun   socket <---> socket   tun
 +------/             \------+
  client               server

它的工作原理很简单:客户端从它的 tun 设备读取字节并将它们写入套接字。两者都简单地实现为对文件描述符执行 Linux 读写。服务器为接收做相反的事情。这有效 full-duplex。这里值得注意的是:

下一版本:

现在,这些端点将不得不使用多个 links 相互通信,而不是单流套接字,例如:

  +----------\           /----------+
  |    .-- tun1A <---> tun1B --.    | 
tun0A ----  tapA <---> tapB  ---- tun0B
  |    '--  socA <---> socB  --'    |
  +----------/           \----------+

由于没有简单的方法来判断端点是否已读取其 tun0 接口上的完整 TCP 段,我打算继续将进入 tun 设备的所有内容简单地视为字节流。我想使用三个 link 中的任何一个将它发送到另一端,而不管原始字节流代表什么。这样一来,原本表示单个 TCP 段的一堆字节可能会使用 4 个单独的包发送到另一端。这些包需要在接收端重新排序,这本身没有问题:只需在我已有的字段中添加第二个字段:a sequence number,对吗?

但是,如果基础 link 不可靠(很可能是)怎么办?如果这些包只是在它们的有效负载中进行了一些交换,我 假设 目前,接收端的网络堆栈能够处理它。但是如果 header 坏了怎么办?端点可能会了解到它应该读取的错误字节数,并且两个端点不同步且没有明显的修复。

我考虑过使用各种确认和超时来实现它,但我并没有放弃对更简单、更优雅的方法的希望(请记住,最终结果不需要可靠)。一些协议使用可靠的 out-of-band 通道(如 TCP)来管理有效负载隧道,但目标是纯粹使用 links。此外,即使 Linux 提供了各种很酷的网络工具(iproute2、netfilter 等),所有这些都应该在 C/C++ 中实现(如果稍微使用这些提供了一个很好的解决方案)。

现在,我缺乏想法,我希望任何人都可以提出一些其他的方法来解决这个问题!如果需要更多信息,请询问,我很乐意写更多 :)

*原来把这个问题放在networkengineering.stackexchange.

编辑 1

我正在考虑用自己的数据报套接字包装器包装每个 "inner link",如上图所示。这个套接字将(希望)至少保证我收到完整的数据报,这样我就不必担心 link 在 trunc 的情况下中断泰德消息。不过,它确实增加了一些开销。

很难理解您对 out-of-sync 的想法,因为网络堆栈处理这个问题。无论如何,有很多方法可以解决这个问题。

  • 在 TCP 中,您有数据偏移量字段,它告诉您负载从哪里开始。
  • 整个TCP段的大小是从IP数据报的“总长度”字段中得到的header。
  • TCP 有一个校验和字段,可以在一定程度上确保检测到错误。 IP 有一个 header 校验和。在 UDP 中,您可以启用或禁用该校验和。
  • 在第 1 层和第 2 层,还有其他技术和方法可确保接收到的帧正确无误:同步化、CRC 等。

您的应用程序需要实现自己的应用程序层协议,并且为了处理在不同通道中以任何顺序传入的消息,您可以使用不同的技术:

  • 首先,有一种方法可以确定应用层收到消息的长度。可能在 TCP 套接字中,您遇到过在 recv 或 read returns 时可能无法读取完整的消息。为了解决这个问题,您可以实施 TLV 格式(类型、长度、值)。
  • 要处理乱序和丢失的消息,您可以查看 IPv4 如何实现分段。这是完全相同的问题。 https://www.rfc-editor.org/rfc/rfc791
  • 您还可以查看 TCP 在不丢弃后续接收到的段的同时处理丢失段的方式
  • 序列号和确认对于拥有可靠的面向连接的虚拟隧道至关重要。