views:

57

answers:

2

I have a problem with a socket library that uses WSAASyncSelect to put the socket into asynchronous mode. In asynchronous mode the socket is placed into a non-blocking mode (WSAWOULDBLOCK is returned on any operations that would block) and windows messages are posted to a notification window to inform the application when the socket is ready to be read, written to etc.

My problem is this - when receiving a FD_READ event I don't know how many bytes to try and recv. If I pass a buffer thats too small, then winsock will automatically post another FD_READ event telling me theres more data to read. If data is arriving very fast, this can saturate the message queue with FD_READ messages, and as WM_TIMER and WM_PAINT messages are only posted when the message queue is empty this means that an application could stop painting if its receiving a lot of data and useing asynchronous sockets with a too small buffer.

How large to make the buffer then? I tried using ioctlsocket(FIONREAD) to get the number of bytes to read, and make a buffer exactly that large, BUT, KB192599 explicitly warns that that approach is fraught with inefficiency.

How do I pick a buffer size thats big enough, but not crazy big?

A: 

You can set your buffer to be as big as you can without impacting performance, relying on the TCP PUSH flag to make your reads return before filling the buffer if the sender sent a smaller message.

The TCP PUSH flag is set at a logical message boundary (normally after a send operation, unless explicitly set to false). When the receiving end sees the PUSH flag on a TCP packet, it returns any blocking reads (or asynchronous reads, doesn't matter) with whatever's accumulated in the receive buffer up to the PUSH point.

So if your sender is sending reasonable sized messages, you're ok, if he's not, then you limit your buffer size such that even if you read into it all, you don't negatively impact performance (subjective).

Aviad P.
Im not worried about a large read returning soon enough. In my context I am performing the recv in response to a FD_READ event so its guaranteed that the recv is going to return something immediately witout any chance of it trying to block. My question is, how can I calculate how big to make the recv buffer so I can drain all pending data in one recv call. I could make it 1Mb, but Moore's law implies that even that will probably be too small one day not so far away, while still being a crazy waste of resources today.
Chris Becke
Just curious, why do you have to calculate it? Can't you loop and get it one chunk at a time?
Aviad P.
+1  A: 

As far as I could ever work out, the value set using setsockopt with the SO_RVCBUF option is an upper bound on the FIONREAD value. So rather than call ioctlsocket it should be OK to call getsockopt to find out the SO_RCVBUF setting, and use that as the (attempted) value for each recv.

Based on your comment to Aviad P.'s answer, it sounds like this would solve your problem.

(Disclaimer: I have always used FIONREAD myself. But after reading the linked-to KB article I will probably be changing...)

brone
Im going to go with SO_RCVBUF as the most sensible buffer size to use.
Chris Becke