I have a question regarding this question ("Asynchronous server socket multiple clients").
Either Microsoft changed the example since Groos answer or I really don't get it - in the example it says:
while (true) {
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener );
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
As I understand the BeginAccept() function is called contiuously in the while(true) loop, only being stopped by and until the AcceptCallback() function is called, because the first thing that happens there is allDone.Set().
But Groo said
The problem with MSDN example is that it allows connection of only a single client (listener.BeginAccept is called only once).
And actually I don't get why the ManualResetEvent allDone is used at all... And I thought the listener.EndAccept(ar) method is blocking anyways.
Is listener.BeginAccept() throwing an exception if called a second time while still running ? But if so why allDone.Set() is before listener.EndAccept(ar)?
And another question:
Can I just call handler.BeginReceive(...), in the ReadCallback(IAsyncResult ar) function after I received a EOF, to wait for more incomming data from the same client?
Can anybody with more experience explain that to me, please?
Thanks !
The sample is confused. The event is not needed. Instead, the accept callback is supposed to issue the next accept operation so that there is always one accept outstanding.
Calling BeginAccept in an unthrottled loop would be incorrect since that would start an unbounded number of outstanding accept operations.
Are you aware that since the introduction of
await
the old APM is obsolete? All of this code has no relevance today.Also, note that most socket code on the web is deeply flawed in different ways.
It's possible the example was in fact updated since that other SO answer was posted. Or it's possible the answerer Groo simply did not fully understand the example himself. In either case, you are correct to observe that his statement that only one client can ever be accepted is incorrect.
I agree with some of what usr wrote, but have a somewhat different take on the whole thing. Also, I think the questions deserve more comprehensive and specific treatment.
First, while I agree that it is superior design generally to issue subsequent calls to
BeginAccept()
in the accept callback method rather than using a loop, there's nothing wrong per se with the implementation in the example. No new call toBeginAccept()
is made until the after completion of the previous call has been signaled; the event handle is used to synchronize the thread whereBeginAccept()
is called with whichever thread winds up handling the completion. The first thread is released only when the previously-issued accept completes, and then only one new call toBeginAccept()
is made before that thread blocks again.It's a bit awkward, but completely kosher. It's possible that the author of that sample figured that since in his console program, he was going to have a thread sitting there idle anyway, he might as well give it something to do. :)
Anyway, to answer question #1: you are correct, the example currently present at that link does allow multiple clients to connect.
Question #2, why is the event handle used, I hope the above explanation has answered that. It's what the example uses to release the thread which is calling
BeginAccept()
, so that it can call it another time once the previous call has completed.Question #3,
EndAccept()
is blocking? Sort of. If you callEndAccept()
before the accept operation has actually completed, then yes. It will block. But in this case, it's being called only when the completion callback has been called. At this point, we can be certain that the call toEndAccept()
will not block. All it's going to do is retrieve the result of the completed operation at that point (assuming no exceptions, of course).Question #4, is it legal to call
BeginAccept()
a second time, beforeEndAccept()
has been called? Yes, even if it were not legal to have multiple accept operations queued up (which it is). Here, the call toBeginAccept()
happens in the completion callback for the firstBeginAccept()
. That is, while the code hasn't calledEndAccept()
yet, the accept operation itself has completed and so it's not even a case of having multiple accept operations outstanding. Receive and send operations are similarly liberal; you can legally call all of those methods multiple times before a completion for any has happened.Question #5, can I call
BeginReceive()
even though I've received<EOF>
? Yes. In fact, this is an area in which the MSDN example is flawed, in that it does not continue receiving once the last of the expected data has been received. In actuality, until a receive completes with 0 bytes it should still always callBeginReceive()
again, whether or not more data is expected, and then handle a completed receive where the byte count is 0 by callingShutdown(SocketShutdown.Both)
at that point to signal acknowledgement of the graceful closure of the connection (assuming it's done sending by that point, at which time it would have already calledShutdown(SocketShutdown.Send)
...if not, it should just useSocketShutdown.Receive
and/or just not call Shutdown at all until it's done sending and it can useSocketShutdown.Both
...SocketShutdown.Receive
doesn't actually do anything significant to the connection itself, so it's reasonable to just wait untilSocketShutdown.Both
is appropriate).In other words, even if the server knows for sure the client isn't going to send any additional data, correct use of the socket API is to still attempt another receive operation, looking for that 0-byte return value that indicates that the client has in fact started to shutdown the connection. Only at that point should the server begin its own shutdown process and close the socket.
Finally, you didn't ask but because usr brought it up: I disagree that this MSDN example has no relevance today. Unfortunately, Microsoft didn't provide a Task-based version of an async API for the
Socket
class. There are other network APIs that support async/await (e.g.TcpClient
/NetworkStream
), but if you want to use theSocket
class directly, you're stuck with the old async models (Socket
has two, both callback-based).You could wrap the synchronous
Socket
methods inTasks
as an alternative to the older API, but then you'd lose the advantage of the I/O completion port-based asynchronous API in the Socket class. Much better would be some kind of Task-compatible wrapper that still uses the asynchronous API underneath, but that's somewhat more complicated to implement and I'm not aware of such a thing at the moment (but it certainly could exist, so that might be worth a bit of web-searching on your part if you'd prefer to use async/await).Hope that helps! I'd apologize for the long answer, but it was a pretty broad question (almost too broad, but to me it seemed still within reasonable SO bounds :) ).