Search code examples
c#multithreadingsocketsasynchronousmanualresetevent

Will ManualResetEvent block the entire program?


I have a program that begins itself by listening for connections. I wanted to implement a pattern in which the server would accept a connection, pass that individual connection to a user class for processing: future packet reception, and handling of the data.

I ran into trouble with the synchronous pattern before I found out that asynchronous use of the Socket class isn't scary. But then I ran into more trouble. It seemed that, in a while (true) loop, since BeginAccept() is asynchronous, the program would constantly move through this loop and eventually run into an OutOfMemoryException. I needed something to listen for a connection, and immediately hand off responsibility of that connection to some other class.

So I read Microsoft's example and found out about ManualResetEvent. I could actually specify when I was ready for the loop to begin listening again! But after reading some questions here on Stack Overflow, I have become confused.

My worry is that even though I have asynchronously accepted a connection, the entire program will block while it's trying to listen for a new connection upon re-entering the loop. This isn't ideal if I'm handling multiple users.

I'm very new to the world of asynchronous I/O, so I would appreciate even the angriest of comments about my vocabulary or a misuse of a phrase.

Code:

static void Main(string[] args)
{
    MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
    MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
    MainSocket.Listen(10);

    while (true)
    {
        Ready.Reset();
        AcceptCallback = new AsyncCallback(ConnectionAccepted);
        MainSocket.BeginAccept(AcceptCallback, MainSocket);
        Ready.WaitOne();
    }
}

static void ConnectionAccepted(IAsyncResult IAr)
{
    Ready.Set();
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}

Solution

  • The Microsoft example, in which they use the old-style WaitHandle based events, will work but frankly it is a very odd and awkward way to implement asynchronous code. I get the feeling that the events are there in the example mainly as a way of artificially synchronizing the main thread so it has something to do. But it's not really the right approach.

    One option is to just not even accept sockets asynchronously. Instead, use the asynchronous I/O for when the socket is connected and use a synchronous loop in the main thread to accept sockets. This winds up being pretty much exactly what the Microsoft sample does anyway, but keeps all of the accept logic in the main thread instead of switching back and forth between the main thread (which starts the accept operation) and some IOCP thread that handles the completion.

    Another option is to just give the main thread something else to do. For a simple example, this could be simply waiting for some user input to signal that the program should shut down. Of course, in a real program the main thread could be something useful (e.g. handling the message loop in a GUI program).

    If the main thread is given something else to do, then you can use the asynchronous BeginAccept() in the way it was intended: you call the method to start the accept operation, and then don't call it again until that operation completes. The initial call happens when you initialize your server, but all subsequent calls happen in the completion callback.

    In that case, your completion callback method looks more like this:

    static void ConnectionAccepted(IAsyncResult IAr)
    {
        Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
        MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
    }
    

    That is, you simply call the BeginAccept() method in the completion callback itself. (Note that there's no need to create the AsyncCallback object explicitly; the compiler will implicitly convert the method name to the correct delegate type instance on your behalf).