如何判断套接字缓冲区是否已满?

如何判断读取套接字缓冲区是满还是写入套接字缓冲区为空?

有没有办法可以在没有系统调用的情况下获得套接字缓冲区的状态?

更新:怎么样:当读取套接字缓冲区已满或写入套接字缓冲区为空时,我想获得回调或信号。 这样我就可以停止处理以允许在线路上发生更多的I / O,因为在线路上发送数据时,I / O限制总是一个问题。

select()调用是检查读缓冲区中是否包含某些内容的方法。 不是当它满了(我想)。

你可以尝试ioctl 。 FIONREAD告诉您可以立即读取多少字节。 如果这与缓冲区大小(您可以通过另一个icotl调用检索和/或设置)相同,则缓冲区已满。 同样,如果您可以编写与输出缓冲区大小一样多的字节,那么输出缓冲区为空。

我没有广泛支持FIONREAD,FIONWRITE和SIOCGIFBUFS(或等价物)。 我不确定我曾经使用过任何一种,虽然我有一种偷偷摸摸的感觉,我出于某种原因在Symbian上使用了类似的function。

调用是否需要内核模式来计算它是特定于平台的。 模糊地试图避免系统调用不是一种有效的优化技术。

一个基本的BSD风格的套接字接口没有提到任何关于读写缓冲区的内容。 什么时候发送缓冲区是否为空? 它当然并不意味着已经在套接字的另一个端点接收到所有数据 – 它可能位于某个路由器的某个地方。 同样,“你的”读缓冲区已满,并不能保证另一端的写操作会阻塞。

一般来说,您只需尽可能多地读/写,并让套接字层处理复杂性。 如果你看到很多I / O都是用小尺寸完成的,那么可能存在一些性能问题。 但请记住,流套接字将一次发送/接收一个包,其中包含一个数据块。 除非设置了TCP_NODELAY,否则不会像NIC上的字节到达字节,并且您最终可能每个字节进行一次读取调用。 它们是以数据包forms到达的,因此很可能一次性可读,也许一次只能达到1k-ish。 你不太可能通过暂缓阅读来加快速度,直到有很多东西需要阅读。 实际上,您可能会使情况变得更糟,因为当您的端点的读取缓冲区已满时,存在传入数据被丢弃的风险,因为无处存储它,导致延迟和重新发送。

使用select和零超时轮询文件描述符 – 如果select表示它是可写的,则发送缓冲区未满。

(哦…… 没有系统调用。不,没有。)

附录:

为了响应您更新的问题,您可以在TCP套接字上使用两个ioctlSIOCINQ返回接收缓冲区中未读数据的数量, SIOCOUTQ返回发送队列中未发送数据的数量。 我不相信这些有任何异步事件通知,这将让你不得不轮询。

我知道这是一个老线程,但为了那些通过搜索引擎偶然发现这一点的人的利益,我将回答这个问题,因为它上面没有真正回答过。

在开始之前,克服系统调用挂起 – 您无法在不切换内核空间的情况下与基于内核的(* nix)网络堆栈进行交互。 您的目标应该是了解堆栈function,以便您可以充分利用系统。

如何判断读取套接字缓冲区是否已满

这部分已被回答 – 你没有回答,因为这不是你应该怎么想的。

如果发送者(严重)将其TCP帧分段(通常是因为没有在输出上缓冲封送数据,并且使用TCP_NDELAY关闭Nagle算法),那么减少系统调用次数的想法是个好主意。 您应该使用的方法包括设置“低水印”进行阅读。 首先,通过使用setsockopt()设置SO_RCVBUF,建立您认为合理的接收缓冲区大小。 然后使用getsockopt()读回实际的读取缓冲区大小,因为您可能无法得到您要求的内容。 :)不幸的是,并非所有实现都允许您再次阅读SO_RCVBUF,因此您的里程可能会有所不同。 接下来,在您想要阅读之前,确定您想要阅读的数据量。 使用setsockopt()设置此大小的SO_RCVLOWAT。 现在,套接字的文件描述符只有在读取至少读取的数据量时才会选择为可读。

或写入套接字缓冲区为空?

这是一个有趣的,因为我最近需要这样做,以确保我的MODBUS / TCP ADU每个都占用自己的TCP帧,这是MODBUS规范所要求的(@steve:控制碎片是你需要知道何时发送的一次缓冲区是空的!)。 就原始海报而言,我非常怀疑他是否真的想要这个,并且相信在开始之前了解发送缓冲区大小会更好,并且在发送期间定期检查发送缓冲区中的数据量,使用已经描述的技术。 这将提供关于所使用的发送缓冲区的比例的更精细的信息,这可以用于更顺利地节省生产。

对于那些仍然对如何检测(异步)发送缓冲区为空(一旦你确定它真的是你想要的)感兴趣的人,答案很简单 – 你设置发送低水位线(SO_SNDLOWAT)等于发送缓冲区尺寸。 这样,当发送缓冲区为空时,套接字的文件描述符将仅选择为可写。

我对问题的回答主要围绕select()的使用,这并非巧合。 在几乎所有情况下(我意识到我现在正在进入宗教领域!)需要移动大量数据(内部和主机间)的应用程序最好构造为单线程状态机,使用选择掩码和基于pselect()的处理循环。 如今,一些操作系统(Linux命名之一)甚至允许您使用文件描述符选择来管理信号处理。 什么奢侈 – 当我还是个男孩…… 🙂

彼得

考虑到套接字的内核缓冲区存在于内核空间中,我怀疑在没有系统调用的情况下有任何方法可以要求大小。
使用系统调用,您可以尝试使用PEEK进行recv。

 ret = recv(fd, buf, len, MSG_PEEK); 

将给出recv但不清空缓冲区。

没有系统调用就不可能做到这一点。 但是系统调用有什么问题?

我认为你的方法存在缺陷/注定的根本原因。 系统不希望告诉您何时读取缓冲区已满/写入缓冲区为空,因为这些事件表明您与系统之间的合同出现故障。 如果事情到达那一点(特别是在读取方向上),那么确保协议栈的平稳运行为时已晚 。 当您最终决定读取缓冲区时,可能会有更多数据到达。 您应该在缓冲区满之前读取缓冲区,这是缓冲I / O的重点。

如果在单独的线程中执行read(),SO_RCVLOWAT可以帮助阻止此读取,直到缓冲区中有足够的数据。 不幸的是,poll()和select()至少在Linux上忽略了这个套接字选项,并且总是检查可用的单字节。

@火焰,

Linux和SO_RCVLOWAT

尊重,我的经验与你的不同。 自FC5以来,我一直在Linux中使用接收缓冲区低水位,在通过IP(UDP和TCP)分发video的产品中,所以我理解充分利用网络堆栈function的重要性。 事实上,Linux是第一个允许你读回低水印的实现之一(有些仍然不允许)。 🙂

你提到poll()和select()是不尊重SO_RCVLOWAT。 只要我记得,我一直在使用pselect(),所以问题可能在于select()和poll()。 在任何情况下,您应该始终使用pselect()或ppoll()(如果可用)优先于旧调用,因为它们可以在您进入/离开调用时以primefaces方式更改程序的信号掩码。 如果您了解这意味着什么,那么您将理解为什么这在商业软件中至关重要。 如果没有,这样的讨论将保证自己的线程。 🙂

彼得