EdwardH
EdwardH

Reputation: 1573

Non-blocking TCP socket handling - How to detect blocking state prior to writing on to the socket?

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.

Upvotes: 1

Views: 3062

Answers (2)

EdwardH
EdwardH

Reputation: 1573

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:

  1. Use of SIOCOUTQ to 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.

  2. Use of select/poll/epoll and adding a small limited buffer per read socket: Initially, all read sockets will be added to the poll, when ready for read, we read it in a limited buffer size, then remove the read socket from the poll and add instead of it the destination socket for writing, when we return to the poll and 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 the poll/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.

Upvotes: 1

ugoren
ugoren

Reputation: 16449

select or poll should be able to give you the information.
I assume you're already using one of them to detect which of your reading sockets has data
When you have a reading socket available for read, replace it with the corresponding writing socket (but put it in the write fds of course), and call select again. Then, if the writing socket is available, you can read and write.

Note that it's possible that the writing socket is ready to get data, but not as much as you want. So you might manage to read 100 bytes, and write only 50.

Upvotes: 1

Related Questions