I’m currently writing a proxy application that reads from one socket and writes on another. Both are set as non-blocking, allowing multiple sockets pairs to be handle.
To control a proper flow between the sockets, the application should NOT read from the source socket if the writing on the target socket may block.
The idea is nice, however I found no way to detect a blocking state of the target socket without first writing to it… and that is not what is needed.
I know of an option to use SIOCOUTQ (using ioctl()) and calculate the remaining buffer, but this seems ugly compared to a simple check if the target socket is ready for writing.
I guess I can also use select() for just this socket, but that is so much waste of a such heavy system call.
Thank you all for the feedback.
Summarizing all comments and answers up until now:
Directly to the question, the only known way to detect that a socket will block on an attempt to write to it is using
select/poll/epoll.The original objective was to build a proxy, which reads from one socket and writes to another, keeping proper balance between them (reading at the same rate of writing and vice versa) and using no major buffering in the application. For this, the following options were presented:
Use of
SIOCOUTQto find out how much buffer is left on the destination socket and transmit no more than that. As pointed out by @ugoren, this has a disadvantage of being unreliable, mainly because in between reading the value, calculating it and attempting to write the actual value may change. It also introduces some issues of busy-waiting if wrongly managed. I guess, if this technique is to be used, it should be followed by a more reliable one for full protection.Use of
select/poll/epolland adding a small limited buffer per read socket: Initially, all read sockets will be added to thepoll, when ready for read, we read it in a limited buffer size, then remove the read socket from thepolland add instead of it the destination socket for writing, when we return to thepolland we detect that the destination socket is ready for writing, we write the buffer, if all data was accepted by the socket, we remove the destination socket from the poll and return the read socket back. The disadvantage here is that we increase the number of system calls (adding/removing to thepoll/select) and we need to keep an internal buffer per socket. This seems to be the preferred approach, and some optimization could be added to try and reduce the call to system calls (for example, trying to write right after reading the read socket, and only if something is left to perform the above).Thank you all again for participating in this discussion, it helped me a lot ordering the ideas.