关于通过(unix-)套接字发送/接收大量数据的另一个困惑
Posted
技术标签:
【中文标题】关于通过(unix-)套接字发送/接收大量数据的另一个困惑【英文标题】:Yet another confustion about sending/recieving large amount of data over (unix-) socket 【发布时间】:2016-12-05 06:51:19 【问题描述】:我有一个 C++ 程序,它从高速摄像机读取帧并将每个帧写入一个套接字(unix 套接字)。每次写入为 4096 字节。每帧大约是 5MB。 (不能保证帧大小是恒定的,但它始终是 4096 字节的倍数。)
有一个从套接字读取的python脚本:每次调用recv
时读取10 * 4096字节。我经常会遇到意想不到的行为,我认为这可以归结为了解以下有关套接字的内容。我相信我的两个程序都处于阻塞模式write
/recv
ing。
理想情况下,我希望我的应用程序尽可能快地写入套接字。如果没有人在读取数据,那么覆盖就可以了。如果有人正在从套接字读取数据但读取速度不够快,我想将所有数据存储在缓冲区中。那么如何在读取缓慢时强制我的套接字增加缓冲区大小?
【问题讨论】:
一个一般的想法,使用 UDP 套接字。如果您使用的是 OpenCV,请参阅 cs.utexas.edu/~teammco/misc/udp_video 您对延迟和抖动的关注程度如何? 【参考方案1】:我可以一次写入整个帧(使用 5MB 数据写入调用)吗?是吗 受到推崇的?速度是这里的主要关注点。
好吧,您当然可以尝试,但是如果对 socket.send() 的调用仅发送您要求它发送的部分字节,请不要太惊讶。特别是,您应该始终检查 socket.send() 的返回值以查看它实际从您那里接受了多少字节,因为该值可能大于零但小于您的字节数传入调用。 (如果它更少,那么您可能需要再次调用 socket.send() 以从缓冲区中发送第一次调用未处理的剩余字节......并根据需要重复;或者您也可以调用socket.sendall() 而不是 socket.send(),这将为您执行必要的循环和重新调用 socket.send() 命令,因此您不必担心它...权衡是 socket.sendall() 可能很长一段时间都不会返回,这取决于您的网络连接速度以及您告诉 socket.sendall() 发送多少数据)
请注意,在发送数据报时,通常会强制执行最大数据包大小;大于该值的数据包将被分割成较小的数据包进行传输(并希望在接收端重新组装),或者它们可能被简单地丢弃。例如,当通过以太网发送 UDP 数据包时,MTU 的大小通常为 1500 字节。通过 Unix 套接字发送时,MTU 可能会比这大,但可能是there will still be a limit。
如果python客户端读失败或读慢于写,是否意味着 一段时间后,套接字上的写操作不会添加到 缓冲?或者,他们会覆盖缓冲区吗?如果没有人在阅读 socket,我不介意覆盖缓冲区。
如果您在流式套接字 (SOCK_STREAM) 上发送,那么如果/当缓冲区填满时,慢速客户端将导致您的服务器的 send() 调用阻塞。如果您在数据报样式的套接字 (SOCK_DGRAM) 上发送并且缓冲区已填满,则“溢出”数据报将被丢弃。
那么我该如何强制我的套接字增加缓冲区大小 阅读速度慢?
您可以通过socket.setsockopt(SOL_SOCKET, SO_SNDBUF, xxx) 设置套接字的发送缓冲区大小。请注意,这通常是提前完成的(例如,在创建套接字之后),而不是尝试“即时”完成以响应速度较慢的阅读器。
【讨论】:
谢谢。我一次性发送 320kb 的数据。我测试了我的应用程序几个小时。读取端使用缓冲区来确保已接收到每一帧。【参考方案2】:这听起来像是一个设计缺陷,您需要通过套接字发送这么多数据才能开始,并且存在阅读器跟不上作者的风险。作为替代方案,您可能需要考虑使用增量编码,在“关键帧”(整个帧)和多个帧之间交替编码为前一帧的增量。您可能还需要考虑将数据写入本地缓冲区,然后在您的 UNIX 域套接字上实现自定义协议,该协议允许读取从给定时间戳开始的帧序列或给定时间戳的单个帧。如果所有读取都通过这样的缓冲区而不是直接从源读取,我想您还可以在该协议中添加额外的编码/压缩选项。此外,如果将数据导出到 UNIX 套接字的服务器应用程序是与读取数据并将其写入缓冲区的应用程序不同的应用程序,则您无需担心数据摄取被慢速读取器阻塞.
【讨论】:
以上是关于关于通过(unix-)套接字发送/接收大量数据的另一个困惑的主要内容,如果未能解决你的问题,请参考以下文章