使用 tcp 重传的可靠 udp

Reliable udp using tcp retransmit

本文关键字:udp tcp 重传 使用      更新时间:2023-10-16

我是发送服务器发送的UDP组播数据(证券交易所数据)的客户端接收者。我连续接收 udp 组播数据包流,顺序编号为 1 到大约 35,000,000,在 6 小时内统一发送。我需要确保在每次说~256个数据包之后定期处理N个数据包集之前,收到所有数据包,例如N。即我需要可靠的 UDP。

使用 TCP 重传来模拟可靠的 UDP。如果任何 udp 数据包丢失/未收到,则使用 tcp 协议通过指定所需的丢失数据包范围(起始编号、结束编号)来请求该数据包。 发送方记录迄今为止通过 UDP 组播发送的所有数据包(证券交易所数据)。因此,发送方将仅通过TCP重新发送接收方通过TCP专门请求的数据包编号。这就是接收器实现UDP可靠性的方式。UDP 丢弃率非常小(小于 0.001%),除非在中午启动 UDP 组播,在这种情况下,所有以前发送的从 1 到大约 N 的 UDP 数据包都需要在 TCP 上重新发送,同时接收 UDP 组播数据包编号 N+1 的实时传输。我不能要求发件人(证券交易所)更改其协议 - 它是固定的。

在CPU 方面实现这一点的有效算法是什么? 问题是速度大哦。我可以使用几个嵌套循环和方法制作一个朴素的算法,但它不一定是最好的。

我正在考虑维护一个数字 N,以确认我已收到 UDP 数据包 1 到 N,以及任何不是下一个预期数据包编号 N+1 的数据包编号 M 将被缓冲,例如 256 个数据包,然后 TCP 将用于请求丢失的号码。然后,在TCP请求被填满后,正常的UDP接收将从最后一个确认的接收号码恢复。

例: 假设接收方接收的 UDP 数据包按以下顺序 {1,2,3,6,7,8,9,10 ...} 在第 3 号数据包之后,下一个数据包是第 6 号数据包。缺少数据包 4 到 5。 因此,丢失的数据包 {4,5} 使用 TCP 请求({4 到 5})请求,并且 {6,7,8,9,10} 被缓冲。10GBaseT LAN 卡上有足够的空间来缓冲 35,000,000 个数据包。 所以:接收UDP {1,2,3},通过TCP请求{4,5}重新填充,继续接收UDP {6,7,8,9,10,...}

我假设既然你使用的是多播,那么这些数据会有多个接收器? (因为如果没有,您可能会使用单播)

因此,如果接收方可以选择请求TCP重新传输他们没有获得的数据包,这意味着传输程序将需要在内存中保留最近发送的UDP数据包的副本,以便当它收到重新传输请求时,它将有请求的数据可用于重新传输。 假设您使用唯一 ID 标记每个数据包,它可以将此数据存储在std::mapstd::unordered_map或类似文件中,以便快速查找。

真正的问题是,发射器应该保留多少旧数据包数据? 理想情况下,它会保留所有内容,因为您永远不知道给定的接收器可能错过了多少并可能想要请求多少;但这需要无限的内存,所以这不是一个现实的选择。 您可能能做的最好的事情就是决定您愿意为此目的占用多少 RAM,并计算表中的字节总数,当它达到限制时,开始从表中丢弃最旧的数据包以将其大小保持在限制以下。

我编写了一个开源库,它基本上使用您描述的技术(组播UDP + TCP-retransmission-to-recover-from-tape-lost)来尽快在多个主机之间同步数据库;我在实现它时学到的一些东西包括:

如果/
  • 如果可以,将数据消息打包成更大的数据包,直到您传输的网络的 MTU(例如,IPv4/以太网为 1388 字节)。 非常小的数据包大小(如 48 字节/数据包)效率低下,因为固定大小的数据包标头占发送/接收的总数据的百分比更大。

  • 仅当发送套接字指示已准备好写入时,才尝试发送。 (即不要假设你永远不会填满套接字的传出数据缓冲区;如果你的流量是"突发"的,你可能会在某个时候填满

    )
  • 通过使 UDP 套接字的发送和接收缓冲区尽可能大来最大程度地减少 UDP 数据包丢失

  • 通过在专用的高优先级线程
  • 中执行所有UDP接收,进一步减少UDP数据包丢失(然后可以将收到的UDP数据路由回正常优先级线程进行进一步处理 - 主要是避免允许接收UDP套接字的传入数据缓冲区溢出,如果可能的话)

  • 对于 TCP 重传部分,请记住,在最坏的情况下,TCP 流可能会减慢到每秒几乎为零字节,因此确保客户端 A 的糟糕 TCP 性能不会阻止与客户端 B、C、D 等之间的 TCP 通信非常重要。 这可以通过非阻塞 I/O 和select()(或poll()或类似)或异步网络或通过多线程来实现;避免阻塞 I/O,除非你正在实现一个每套接字线程模型(并且可能也避免该模型,因为无限期阻塞的线程很难完全关闭)

  • 想想在什么情况下(如果有的话)客户端从不接收特定数据包是可以接受的;是否有可以接受的情况? 还是整个系统必须停止,直到每个接收方都收到组中的每个数据包,无论这可能需要多长时间?

  • 如果你想变得非常花哨,你可以研究跨数据包对数据进行编码的前向纠错算法,这样接收方仍然可以解码所有数据,即使它从未收到(达到一定百分比的)数据包。 这使得重新传输请求的需求降低,代价是使所有数据包略大。