简体   繁体   English

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

[英]How can I tell if a socket buffer is full?

How can I tell if a read socket buffer is full or a write socket buffer is empty? 如何判断读套接字缓冲区已满或写套接字缓冲区为空?

Is there a way I can get the status of a socket buffer without a system call? 有没有一种方法可以在没有系统调用的情况下获取套接字缓冲区的状态?

UPDATE: How about this: I'd like to get a callback or signal when either the read socket buffer is full or the write socket buffer is empty. 更新:怎么样:当读套接字缓冲区已满或写套接字缓冲区为空时,我想获取一个回调或信号。 This way I can stop processing to allow more I/O to occur on the wire, since being I/O bound is always an issue when sending data on the wire. 这样,我就可以停止处理以允许在线路上发生更多的I / O,因为在线路上发送数据时,始终受I / O约束是一个问题。

The select() call is how you check if the read buffer has something in it. select()调用是您检查读取缓冲区中是否包含某些内容的方式。 Not when it is full (I think). 不是,当它装满(我认为)。

Poll the file descriptor with select and a zero timeout - if select says it's writeable, the send buffer isn't full. 使用select和零超时来轮询文件描述符-如果select表示可写,则发送缓冲区未满。

(Oh... without a system call. No, there isn't.) (哦... 没有系统调用。不,没有。)

Addendum: 附录:

In response to your updated question, you can use two ioctl s on the TCP socket: SIOCINQ returns the amount of unread data in the recieve buffer, and SIOCOUTQ returns the amount of unsent data in the send queue. 为了回答您的更新问题,您可以在TCP套接字上使用两个ioctlSIOCINQ返回接收缓冲区中的未读数据量, SIOCOUTQ返回发送队列中的未发送数据量。 I don't believe there's any asynchronous event notification for these though, which will leave you having to poll. 我不相信这些有任何异步事件通知,这将使您不得不轮询。

I know this is an old thread, but for the benefit of those who stumble onto this via search engine, I will answer the question, as it hasn't really been answered above. 我知道这是一个旧线程,但是对于那些通过搜索引擎偶然发现的人来说,我会回答这个问题,因为上面没有真正回答过。

Before I start, get over the system call hangup - you cannot interact with kernel-based (*nix) network stacks without switching in and out of kernel space. 在开始之前,请克服系统调用挂断的问题-如果不切换内核空间,则无法与基于内核的(* nix)网络堆栈进行交互。 Your goal should be to understand the stack features, so you can get the best out of your system. 您的目标应该是了解堆栈功能,以便从系统中获得最大的收益。

How can I tell if a read socket buffer is full 如何判断读取的套接字缓冲区是否已满

This part has been answered - you don't because it's not how you should be thinking. 这部分已经回答-您没有这样做,因为这不是您应该怎么想的。

If the sender is (badly) fragmenting it's TCP frames (usually due to not buffering marshaled data on output, and having the Nagle algorithm turned off with TCP_NDELAY), your idea of reducing the number of system calls you make is a good idea. 如果发件人(严重)对它的TCP帧进行了分段(通常是由于未在输出中缓存封送处理的数据,并且使用TCP_NDELAY关闭了Nagle算法),那么减少系统调用次数的想法是个好主意。 The approach you should be using involves setting a "low watermark" for reading. 您应该使用的方法涉及设置“低水印”以供阅读。 First, you establish what you think is a reasonable receive buffer size by setting SO_RCVBUF using setsockopt(). 首先,通过使用setsockopt()设置SO_RCVBUF来确定您认为合理的接收缓冲区大小。 Then read back the actual read buffer size using getsockopt(), as you might not get what you ask for. 然后,使用getsockopt()读回实际的读取缓冲区大小,因为您可能无法获得所需的内容。 :) Unfortunately, not all implementations allow you to read SO_RCVBUF back again, so your mileage may vary. :)不幸的是,并非所有实现都允许您再次读取SO_RCVBUF,因此您的工作量可能会有所不同。 Next, decide how much data you want to be present for reading before you want to read it. 接下来,在读取数据之前,决定要显示多少数据以供读取。 Set SO_RCVLOWAT with this size, using setsockopt(). 使用setsockopt()将SO_RCVLOWAT设置为此大小。 Now, the socket's file descriptor will only select as readable when there is at least that amount of data read to read. 现在,仅当读取的数据量至少等于读取量时,套接字的文件描述符才会选择为可读。

or a write socket buffer is empty? 还是写套接字缓冲区为空?

This is an interesting one, as I needed to do this recently to ensure that my MODBUS/TCP ADU's each occupied their own TCP frames, which the MODBUS specification requires (@steve: controlling fragmentation is one time you do need to know when the send buffer is empty!). 这是一个有趣的问题,因为我最近需要这样做,以确保我的MODBUS / TCP ADU各自占用自己的TCP帧,这是MODBUS规范要求的(@steve:控制分段是您确实需要知道何时发送的时间)缓冲区为空!)。 As far as the original poster is concerned, I doubt very much that he really wants this, and believe he would be much better served knowing the send buffer size before he starts, and checking the amount of data in the send buffer periodically during sending, using techniques already described. 就原始发布者而言,我非常怀疑他是否真的想要这样做,并且相信他在开始之前就知道发送缓冲区的大小,并在发送过程中定期检查发送缓冲区中的数据量会更好。使用已经描述的技术。 That would provide finer-grained information about the proportion of the send buffer used, which could be used to throttle production more smoothly. 这将提供有关所使用的发送缓冲区的比例的更细粒度的信息,这些信息可用于更平稳地限制生产。

For those still interested in how to detect (asynchronously) when the send buffer is empty (once you're sure it's really what you want), the answer is simple - you set the send low-watermark (SO_SNDLOWAT) equal to the send buffer size. 对于仍然对如何(异步)检测发送缓冲区为空(一旦您确定它确实是您想要的)的方式仍然感兴趣的人,答案很简单-您将发送低水印(SO_SNDLOWAT)设置为等于发送缓冲区尺寸。 That way the socket's file descriptor will only select as writable when the send buffer is empty. 这样,套接字的文件描述符将仅在发送缓冲区为空时选择为可写。

It's no coincidence that my answers to your questions revolve around the use of select(). 我对您的问题的回答围绕使用select()并非偶然。 In almost all cases (and I realize I'm heading into religious territory now!) apps that need to move a lot of data around (intra- and inter-host) are best structured as single-threaded state machines, using selection masks and a processing loop based around pselect(). 在几乎所有情况下(并且我意识到我现在正进入宗教领域!),需要在主机内部和主机之间移动大量数据的应用程序最好使用选择掩码和单线程状态机进行结构化基于pselect()的处理循环。 These days some OS's (Linux to name one) even allow you to manage your signal handling using file descriptor selections. 如今,某些操作系统(Linux仅举一例)甚至允许您使用文件描述符选择来管理信号处理。 What luxury - when I was a boy... :) 什么奢侈品-当我还是个男孩的时候::)

Peter 彼得

You can try ioctl . 您可以尝试ioctl FIONREAD tells you how many bytes are immediately readable. FIONREAD告诉您立即可以读取多少个字节。 If this is the same as the buffer size (which you might be able to retrieve and/or set with another icotl call), then the buffer is full. 如果这与缓冲区大小相同(您可以通过另一个icotl调用来检索和/或设置),则缓冲区已满。 Likewise, if you can write as many bytes as the size of the output buffer, then the output buffer is empty. 同样,如果您可以写入与输出缓冲区大小一样多的字节,则输出缓冲区为空。

I don't how widely supported FIONREAD, FIONWRITE, and SIOCGIFBUFS (or equivalents) are. 我没有广泛支持FIONREAD,FIONWRITE和SIOCGIFBUFS(或等效文件)。 I'm not sure I've ever used any of them, although I've a sneaky feeling I've used similar functionality on Symbian for some reason or other. 我不确定是否曾经使用过它们中的任何一个,尽管我有一个偷偷摸摸的感觉,出于某种原因我已经在Symbian上使用了类似的功能。

Whether the call needs kernel mode to compute this is platform-specific. 调用是否需要内核模式来计算,这是特定于平台的。 Vaguely trying to avoid system calls is not a valid optimisation technique. 模糊地尝试避免系统调用不是有效的优化技术。

A basic BSD-style sockets interface doesn't say anything much about read and write buffers. 一个基本的BSD风格的套接字接口并没有说明读写缓冲区。 When does it matter whether the send buffer is empty? 什么时候发送缓冲区是否为空? It certainly doesn't mean that all the data has been received at the other endpoint of the socket - it could be sitting in some router somewhere. 当然,这并不意味着所有数据都已在套接字的另一个端点接收到-它可能位于某个路由器中的某个位置。 Likewise, "your" read buffer being full doesn't guarantee that a write at the other end will block. 同样,“您的”读取缓冲区已满也不能保证另一端的写操作会阻塞。

Generally speaking, you just read/write as much as you can and let the sockets layer handle the complexity. 一般而言,您只需进行尽可能多的读/写操作,然后让套接字层处理复杂性即可。 If you're seeing a lot of I/O completed with tiny sizes then maybe there's some performance problem. 如果您看到许多以很小的尺寸完成的I / O,则可能存在一些性能问题。 But remember that a stream socket will send/receive a packet at a time, containing a block of data. 但是请记住,流套接字将一次发送/接收一个包含数据块的数据包。 Unless TCP_NODELAY is set, it's not as though bytes are arriving by ones at the NIC, and you might end up making one read call per byte. 除非设置了TCP_NODELAY,否则字节就好像不是一个字节到达NIC一样,您可能最终会每个字节进行一次读取调用。 They're arriving in packets, so most likely will become readable all at once, perhaps 1k-ish at a time. 它们以数据包的形式到达,因此很可能一次全部可读,一次一次可能达到1千。 You're unlikely to be able to speed things up by holding off reading until there's a lot to read. 在阅读大量内容之前,您不太可能通过推迟阅读来加快速度。 In fact you might make it worse, because by the time your endpoint's read buffer is full, there's a risk that incoming data is being discarded because there's nowhere to store it, resulting in delays and re-sends. 实际上,您可能会变得更糟,因为到端点的读取缓冲区已满时,由于没有地方存储输入数据,因此存在丢弃传入数据的风险,从而导致延迟和重新发送。

Taking in account that the kernel buffer for sockets lives in kernelspace I doubt there is any way of asking for the size without a syscall. 考虑到套接字的内核缓冲区位于内核空间中,我怀疑没有系统调用是否可以通过任何方式来请求大小。
With syscalls you can try recv with PEEK. 通过syscall,您可以尝试使用PEEK接收。

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

Will give do the recv but without emptying the buffer. 将给出recv,但不清空缓冲区。

That is not possible without a syscall. 没有syscall,这是不可能的。 But what's the problem with syscalls? 但是系统调用有什么问题?

I think there's a fundamental reason why your approach is flawed/doomed. 我认为您的方法有缺陷/有缺陷是有根本原因的。 The system doesn't want to tell you when the read buffer is full / write buffer is empty because those events indicate a break down in the contract between you and the system. 系统不想告诉您读取缓冲区何时已满/写入缓冲区为空,因为这些事件表明您与系统之间的合同发生了故障。 If things get to that point (particularly in the read direction) it is too late for you to ensure smooth operation of the protocol stack. 如果事情到了这一点(特别是在读取方向上),对于您来说确保协议栈的平稳运行为时已晚 Some more data might arrive while you are finally deciding to read the buffer. 当您最终决定读取缓冲区时,可能还会有更多数据到达。 You should be reading the buffer before it gets full , that's the whole point of buffered I/O. 您应该在缓冲区变满之前读取缓冲区,这就是缓冲的I / O的全部内容。

If you do read()s in separate thread, SO_RCVLOWAT can help block this read until there is enough data in buffer. 如果在单独的线程中执行read(),则SO_RCVLOWAT可以帮助阻止该读取,直到缓冲区中有足够的数据为止。 Unfortunately, poll() and select() ignore this socket option at least on Linux and always check for single byte available. 不幸的是,poll()和select()至少在Linux上会忽略此套接字选项,并始终检查单个字节是否可用。

@blaze, @火焰,

Linux and SO_RCVLOWAT Linux和SO_RCVLOWAT

With respect, my experience differs from yours. 尊重方面,我的经历与您不同。 I have been using receive buffer low watermarks in Linux since FC5, in products which distribute video over IP (both UDP & TCP), so I understand how important it is to make the most of your network stack features. 自FC5以来,我一直在Linux中使用接收缓冲区低水位标记,该产品在通过IP分发视频(UDP和TCP)的产品中使用,因此,我了解充分利用网络堆栈功能的重要性。 In fact, Linux was one of the first implementations to allow you to read back the low watermark (and some still don't allow that). 实际上,Linux是最早允许您回读低水印的实现之一(有些仍然不允许这样做)。 :) :)

You mention poll() and select() as failing to respect the SO_RCVLOWAT. 您提到poll()和select()是因为不遵守SO_RCVLOWAT。 I have been using pselect() for as long as I can remember, so maybe the problem is with select() and poll(). 据我所知,我一直在使用pselect(),所以问题可能出在select()和poll()上。 In any case, you should always use pselect() or ppoll(), where available, in preference to the older calls, because they can atomically alter the program's signal mask as you enter/leave the call. 在任何情况下,你应该总是使用PSELECT()或ppoll(),如果有的话,优先于旧的来电,因为当你进入他们可以改变原子程序的信号掩码/结束通话。 If you understand what that means, then you will appreciate why this is critical in commercial software. 如果您理解这意味着什么,那么您将理解为什么这对于商业软件至关重要。 If not, such a discussion would warrant its own thread. 如果没有,这样的讨论将有其根据。 :) :)

Peter 彼得

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM