> - Call BeginReceive() on all the sockets, passing a callback function to
> handle incoming data
> - Do *not* call EndReceive() in the callback function so that it will be
> triggered again when new data arrives (?)
No, you DO call EndReceive() in the callback. You must, in general for
any Begin*/End* pair, in order to get any return value or out
parameters, and in order to free resources associated with the
IAsyncResult. You call BeginReceive() again in the callback once you're
done processing / queuing for processing the data you've just received.
> - When the application needs to terminate, call Close() on each socket
> and skip the EndReceive() call.
You should call Shutdown() and close both sides of the connection
instead. You can safely call Shutdown() on any thread, as long as the
socket isn't closed. That will cause the Receive() to receive 0 bytes,
which is part of the normal clean TCP shutdown sequence. You should call
Close() in a thread that logically owns the socket, or at the very least
in a thread which isn't logically blocking on the socket (I'm including
a pending callback from a Begin* call as "logically blocking"). So,
typically, you'll call Close() on the socket in the callback when you
receive 0 bytes.
> I think the MSDN documentation on this is rather scarce. What if, for
> example, new data arrives while my callback is executing? Will the
> callback be entered again immediately after it finishes?
You need to play with it, to get a feel for how it works. I advise
writing a test application pair with socket server & client, in order to
get a feel for how it all works. You may need to develop some
abstractions to get it all to work together nicely, I know I did.
Coordinating clean shutdowns and handling exceptions gracefully are
probably the two harder parts of asynchronous socket programming, IMHO,
once you've gotten your head around the continuation-passing-style
nature of callback-based .NET async.
-- Barry

Signature
http://barrkel.blogspot.com/