julzzzz
julzzzz

Reputation: 273

c# AsyncSockets recursion in when reading from client socket

I was following the MSDN tutorial on creating an Async Socket Server (here). I needed for the server to be able to continuously listen to messages from the client and after looking at the answers here and here i ended up with something like this:

        public static void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;

            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            { 
                state.sb.Append(Encoding.ASCII.GetString(
                    state.buffer, 0, bytesRead));

                content = state.sb.ToString();
                if (content.IndexOf("<EOF>") > -1)
                {
                    // Message received, do something 
                    // ...
                }
                else
                {
                    // Not all data received. Get more
                    // ...
                }
            }

            // Continue waiting
            handler.BeginReceive(state.buffer, 0, SocketState.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);            
        }

My question is, would this approach grow the stack infinitely since it has a recursive call to ReadCallback()?

Upvotes: 3

Views: 455

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70652

My question is, would this approach grow the stack infinitely since it has a recursive call to ReadCallback()?

No, it would not. By definition, a recursive call would involve a call to another method which would directly or indirectly involve calling the current method while that call is still in progress.

When using any of the many asynchronous techniques available for socket I/O, when you initiate a new receive operation — e.g. by calling the Socket.BeginReceive() method — the method you are calling will not normally cause the current method to be called again while the call is in progress. The current method will be called later, when there is more data to be received.

There is however an important caveat:

  • Just because the call isn't recursive, doesn't mean that your callback can't be called re-entrantly. That is, if data's available soon enough, a different thread could call your callback to handle a new buffer of received data, before the current invocation of the callback has completed.

Typically, this is addressed by not calling BeginReceive() again until the end of the callback (just as the code example you posted does), after all of the data handling has been done. This ensures that even if the method does get called re-entrantly, the only thing left for the current invocation to do is return to the caller, and so it won't conflict with a subsequent call.

There is another theoretical "gotcha", but I'm reasonably certain it doesn't apply here. .NET uses IOCP to implement asynchronous I/O for the sockets API. By default, IOCP will always assign a new thread to completions, which prevents recursion. But, if the IOCP client uses the FILE_SKIP_COMPLETION_PORT_ON_SUCCESS option, IOCP could call the completion callback recursively, without queuing a completion to the completion port, should it happen that data's already available and waiting to be read when the I/O operation is initiated.

I don't think that .NET uses this option, and so you wouldn't run into that. But note that even if you did, the recursion is unlikely to get very deep, because the CPU can generally process data far faster than it can be sent over any I/O device, and particularly a network adapter. It would normally take only another callback or two for the receive operations to catch up with the available data.

The bottom line is that as long as you implement your code as you've done above, where you don't initiate a new receive operation until you're completely done processing the current completed operation, you should have nothing to worry about.

Upvotes: 3

Related Questions