关于发送/接收大量数据(UNIX-)插座的另一种融合

Yet another confustion about sending/recieving large amount of data over (unix-) socket

本文关键字:UNIX- 插座 融合 另一种 数据 于发送      更新时间:2023-10-16

我有一个C 程序,该程序读取高速相机的帧,并将每个帧写入插座(UNIX套接字)。每个写入为4096字节。每个帧大约为5MB。(不能保证帧大小会恒定,但始终是4096字节的倍数。)

有一个python脚本,可以从插座读取:10 * 4096字节在recv的每个呼叫中。通常,我会得到意外的行为,我认为这归结为了解以下有关插座的行为。我相信我的两个程序均为write/recv在阻塞模式下。

  • 我可以一次编写整个帧(用5MB的数据编写呼叫)吗?推荐吗?速度是主要问题。
  • 如果Python客户端未能读取或读取慢于写,这是否意味着在插座上写入操作后,不会添加到缓冲区中?或者,他们会覆盖缓冲区吗?如果没有人在阅读插座,我就不介意覆盖缓冲区。

理想情况下,我希望我的应用程序可以尽可能快地写入套接字。如果没有人正在阅读数据,那么覆盖是可以的。如果有人从插座读取数据,但读取不够快,我想将所有数据存储在缓冲区中。那么,当阅读缓慢时,我该如何强制插座以增加缓冲尺寸?

我可以一次编写整个帧(用5MB的数据写入呼叫)吗?是吗 受到推崇的?速度是主要问题。

好吧,您当然可以尝试,但是如果呼叫socket.send.send()仅发送您要求它发送的字节的一部分,请不要太惊讶。特别是,您应始终检查socket.Send.send()的返回值,以查看您从您接受多少个字节实际上接受了,因为该值可能大于零,但小于您的字节数。传递给电话。(如果较少,那么您可能需要再次致电socket.send(),以从您的缓冲区中发送剩余的字节,而该字节不是第一个通话来处理的...并根据需要重复;或者您可以致电socket.sendall()而不是socket.send.send.send.send.send(),这将对您进行必要的循环和重新打击。是socket.sendall()可能不会长时间返回,具体取决于网络连接的速度以及您告诉socket.sendall()发送的数据)

请注意,在发送数据报时,要执行最大数据包大小是常见的;更大的数据包将被碎片分成较小的数据包以进行传输(并希望在接收侧重新组装),或者可以简单地丢弃它们。例如,在通过以太网发送UDP数据包时,通常的MTU为1500字节。在发送UNIX插座时,MTU可能大于该插座,但可能仍然会有一个限制。

如果Python客户端未能读或阅读慢于写,这是否意味着 一段时间后,在插座上写操作不会添加到 缓冲?或者,他们会覆盖缓冲区吗?如果没有人正在阅读 插座,我不介意覆盖缓冲区。

如果要在流式套接字(SOCK_STREAM)上发送,则慢速客户端会导致服务器的send()调用to Block to Block如果/何时缓冲区填充。如果您要在数据报风格的套接字(SOCK_DGR)上发送和缓冲区填充,则将简单地丢弃"溢出"数据报。

那么,当我如何强制插座以增加缓冲区的大小 阅读很慢?

您可以通过socket.setsockopt(sol_socket,so_sndbuf,xxx)设置插座的发送缓冲器大小。请注意,这通常是事先进行的(例如,在创建插座后立即进行),而不是为了响应缓慢的读取器而尝试"飞行"。

听起来像是一个设计缺陷,您需要在插座上发送大量数据以开始,并且存在这种读者不跟上作者的风险。作为替代方案,您可能需要考虑使用Delta编码,其中您可以在"键帧" S(整帧)和从先前框架中编码为deltas的多个框架之间进行交替。您可能还需要考虑将数据写入本地缓冲区,然后在Unix域套接字上,实现自定义协议,该协议允许在给定时间戳或给定时间戳的单个帧开始读取一系列帧。如果所有读取都通过此类缓冲区而不是直接从源中进行,我想您还可以在该协议中添加其他编码/压缩选项。另外,如果将数据导出到UNIX套接字的服务器应用程序与正在读取数据并将其写入缓冲区的应用程序是一个单独的应用程序,则您无需担心慢读者阻止数据摄入的数据。