Waiting for incoming data on multiple sockets

M

Markus Ewald

Hi!

Using the System.Net.Sockets.Socket class, how am I supposed to wait for
incoming data?

If, for example, I've got a large number of connections that all need to
be monitored for incoming data, do I have to call BeginReceive() for
each socket (possibly producing 50 background threads assuming a naive
socket implementation) and then spawn another 50 threads myself for
blocking on the EndReceive() methods of all the sockets?


Back in C++ I had a shared monitoring thread which blocked by calling
WaitForMultipleObjects() on an array of all the sockets.

I think I could reimplement this concept in .NET using the WaitHandles
provided by the AsyncResult class that is returned from
Socket.BeginReceive(). Is that the way to go?

Thanks,
-Markus-
 
B

Barry Kelly

Markus Ewald said:
Using the System.Net.Sockets.Socket class, how am I supposed to wait for
incoming data?

If, for example, I've got a large number of connections that all need to
be monitored for incoming data, do I have to call BeginReceive() for
each socket (possibly producing 50 background threads assuming a naive
socket implementation)

The socket implementation is not naive in that way, it uses asynchronous
I/O instead.
and then spawn another 50 threads myself for
blocking on the EndReceive() methods of all the sockets?

I personally find it much easier to pass a callback to the BeginReceive
method. That way, a thread will be allocated from the threadpool only
when some data arrives.
Back in C++ I had a shared monitoring thread which blocked by calling
WaitForMultipleObjects() on an array of all the sockets.

That sounds like a Windows-ism for the BSD select() call. You can do
that via Socket.Select, if you wish - it won't be as performant as the
callback, though, and you have a potential socket starvation problem for
sockets towards the end of the array.
I think I could reimplement this concept in .NET using the WaitHandles
provided by the AsyncResult class that is returned from
Socket.BeginReceive(). Is that the way to go?

I wouldn't recommend it, no. In C# 2.0 I'd use anonymous delegates to
simulate continuation passing style code, passing the anonymous delegate
as the callback to the BeginReceive method.

-- Barry
 
C

Chris Mullins

Markus Ewald said:
If, for example, I've got a large number of connections that all need to
be monitored for incoming data, do I have to call BeginReceive() for each
socket (possibly producing 50 background threads assuming a naive socket
implementation) and then spawn another 50 threads myself for blocking on
the EndReceive() methods of all the sockets?

Call BeginReceive on all 50 sockets, and pass in a callback function. For
good luck, pass in the actual socket as state.

When your callback hits, call EndReceive, pull out the data, and proceed.

For more details on this than you ever wanted:
http://makeashorterlink.com/?R2EE5246D
 
M

Markus Ewald

Chris said:
Call BeginReceive on all 50 sockets, and pass in a callback function. For
good luck, pass in the actual socket as state.

When your callback hits, call EndReceive, pull out the data, and proceed.

Thanks Barry and Chris, sounds like the callback is the way to go.

Just to sum it up:
- 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 (?)
- When the application needs to terminate, call Close() on each socket
and skip the EndReceive() call.

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?

-Markus-
 
B

Barry Kelly

Markus Ewald said:
- 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top