发送使用管道数据到多个插座,三通()和拼接()(Send data to multiple sock

2019-08-05 20:05发布

我复制与三通“主”管道()使用拼接写入多个插座()。 当然这些管道会以不同的速度取决于多少我可以拼接()到目标插槽得到清空。 所以,当我下一次再去数据添加到“主”管,然后三通()的话,我可能有一种情况,我可以写64KB到管道,但只有4KB三通的“奴隶”的管道之一。 我猜,然后,如果我剪接()中的所有“主”管插座的,我将永远无法三通()剩余的60KB到该从属管。 真的吗? 我想我可以跟踪一个tee_offset(从0开始),我设置为“unteed”数据开始的,然后不拼接()过去吧。 因此,在这种情况下,我会设置tee_offset为4096,而不是拼接不止于此,直到我能够把它发球台到所有其他管道。 我在这里在正确的轨道上? 任何提示/我警告?

Answer 1:

如果我理解正确的话,你已经得到了你想要复用多个插座数据的实时一些来源。 你有一个单一的“源”管迷上了一切生产真实数据,并且你已经有了超过您要发送的数据的每个插座“目的地”的管道。 你在做什么用tee()以从源头上管的数据复制到每一个目的地管道和splice()它从目标管道插座复制自身。

你要打到这里的根本问题是,如果插座的一个根本无法跟上 - 如果你生产数据的速度比你可以把它,那么你将有问题。 这是不是与您使用的管道,它只是一个根本性的问题。 所以,你要选择一个策略,在这种情况下,以应付 - 我建议处理这个,即使你不希望它是共同的,因为这些东西往往拿出后咬你。 你的基本选择是要么关闭有问题的插座,或直到它清除其输出缓冲跳过数据 - 后者选择可能更适合用于音频/视频流,例如。

关系到你使用管道的问题,然而,就是在Linux上管道缓冲区的大小是不太灵活。 它默认为64K因为Linux 2.6.11(的tee()在2.6.17添加通话) -看到管手册页 。 由于2.6.35此值可通过改变F_SETPIPE_SZ选项fcntl()见的fcntl手册页 )到由指定的限制/proc/sys/fs/pipe-size-max ,但缓冲是仍更尴尬变化点播比用户空间动态分配的方案将是。 这意味着,你的应对缓慢插座能力将受到一定的限制 - 这是否是可以接受的取决于在此等着接收,并能够发送数据的速率。

假设这个缓冲策略是可以接受的,你在你的前提是你需要跟踪多少数据的每个目的地管道已经从源消耗正确的,这是唯一安全的丢弃所有目的地的管道已经使用的数据。 这在一定程度上通过复杂的,因为tee()不具有偏移的概念-你只能从管开始复制。 这样做的结果是,你可以在最慢的插座的速度只复制,因为你不能使用tee()直到一些数据已经从源消耗复制到目的地管道,你可以不做到这一点 ,直到所有的插座有你即将消耗数据。

你如何处理这取决于你的数据的重要性。 如果你真的需要的速度tee()splice()和你有信心,慢套接字将是一个非常罕见的事件,你可以做这样的事情(我假设你正在使用非阻塞IO和单个线程,但类似的东西也多线程工作):

  1. 确保所有管道是非阻塞(使用fcntl(d, F_SETFL, O_NONBLOCK)使每个文件描述符非阻塞)。
  2. 初始化一个read_counter每个目的地管变量为零。
  3. 使用类似的epoll()等到有什么东西在源管。
  4. 遍历所有目标管道,其中read_counter为零,调用tee()将数据传送到每一个。 请确保你通过SPLICE_F_NONBLOCK的标志。
  5. 增量read_counter由转移量为每个目的地管tee() 跟踪结果最低值。
  6. 找到的最低结果值read_counter -如果这是非零,则丢弃来自源管道数据的量(使用splice()调用与目的地打开上/dev/null ,例如)。 丢弃数据后,减去丢弃的量read_counter上的所有配管(因为这是最低的值,那么这不会导致其中的任何变负)。
  7. 从第3步重复。

注意:这是我绊倒了,在过去的一件事是, SPLICE_F_NONBLOCK影响是否tee()splice()的管道操作是非阻塞的,和O_NONBLOCK你设置fnctl()会影响是否与其他调用的相互作用(例如read()write()是非阻塞的。 如果你想要的一切是非阻塞的,均设置。 还记得让你的插座无阻塞或splice()调用数据(除非这就是你想要什么,如果你使用一个线程的方式)转移至他们可能会阻止。

正如你所看到的,这种策略有一个重大问题 - 只要一个插座块了,一切都停止 - 对于插座的目的地管道将填满,然后源管会变得迟钝。 所以,如果你到达那里的舞台tee()返回EAGAIN在第4步,那么你会想要么关闭套接字,或者至少是“断开”它(即把它拿出你的循环),使得你不写别的它,直到它的输出缓冲区为空。 这取决于您选择从具有其位数据流可以恢复是否跳过。

如果你想更优雅地应对网络延迟,那么你会需要做更多的缓冲,这将涉及任何用户空间的缓冲区(其中相当否定的优点tee()splice()或者是基于磁盘的缓冲器。 基于磁盘的缓存几乎肯定会比用户空间的缓冲显著慢,因此不宜因为想必你想了很多的速度,因为你选择了tee()splice()摆在首位,但我提它的完整性。

有一件事,如果你最终在任何时候插入从用户空间数据值得一提的是vmsplice()可以从用户空间进行“收集输出”到管道,以类似的方式来调用writev()调用。 如果你正在做足够的缓冲,你已经(如果您使用的是池分配器的方法例如)多个不同分配的缓冲区间分割你的数据,这可能是有用的。

最后,你能想象使用的“快”方案之间交换插槽tee()splice()如果他们未能跟上,在移动他们较慢的用户空间缓冲。 这将您的实现复杂化,但如果你正在处理大量的连接而其中只有一个非常小的比例是慢则表示仍在减少复制到多数民众赞成有点涉及用户空间的量。 但是,这永远只能是一个短期措施来应对暂时性的网络问题 - 正如我所说本来,你已经有了一个根本性的问题,如果你的套接字是比你慢的来源。 你会最终击出了一些缓冲限制,并需要跳过数据或关闭连接。

总体来说,我会仔细考虑你为什么需要的速度tee()splice()并说明是否为您的使用情况,只需用户空间缓冲在内存或磁盘上会比较合适。 如果你有信心,速度永远是高的,然而,有限的缓冲是可以接受的话,我上面介绍的方法应该工作。

此外,有一件事我应该提到的是,这会让你的代码非常Linux特有 - 我不知道这些电话是在其他Unix变种支持的。 该sendfile()调用超过限制splice()但可能是相当更加便于携带。 如果你真的想要的东西是便携式的,坚持到用户空间缓冲。

让我知道如果有什么事,我已经介绍了,你想对更多的细节。



文章来源: Send data to multiple sockets using pipes, tee() and splice()