Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Microsofts Asynchronous Server Socket Example

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 !

like image 384
fose Avatar asked Nov 01 '14 17:11

fose


1 Answers

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 to BeginAccept() is made until the after completion of the previous call has been signaled; the event handle is used to synchronize the thread where BeginAccept() 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 to BeginAccept() 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 call EndAccept() 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 to EndAccept() 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, before EndAccept() has been called? Yes, even if it were not legal to have multiple accept operations queued up (which it is). Here, the call to BeginAccept() happens in the completion callback for the first BeginAccept(). That is, while the code hasn't called EndAccept() 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 call BeginReceive() again, whether or not more data is expected, and then handle a completed receive where the byte count is 0 by calling Shutdown(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 called Shutdown(SocketShutdown.Send)...if not, it should just use SocketShutdown.Receive and/or just not call Shutdown at all until it's done sending and it can use SocketShutdown.Both...SocketShutdown.Receive doesn't actually do anything significant to the connection itself, so it's reasonable to just wait until SocketShutdown.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 the Socket class directly, you're stuck with the old async models (Socket has two, both callback-based).

You could wrap the synchronous Socket methods in Tasks 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 :) ).

like image 156
Peter Duniho Avatar answered Oct 19 '22 06:10

Peter Duniho