edmz
edmz

Reputation: 8494

How to wait for a connection asynchronously?

I've designed a class which wraps a Socket and allows sending and receiving through/from it. Since accepting, sending and receiving are done through the UI, I don't want them to block it thus I used their asynchronous versions.

Here's the relevant code:

public void Accept()
{
     // ...
     // where resultAccept is a IAsyncResult
    resultAccept = listener.BeginAccept(new AsyncCallback(AcceptCallback), null);
}

private void AcceptCallback(IAsyncResult ar)
{
    connectedSocket = listener.EndAccept(ar);
}

public string Receive()
{
    char[] data = new char[128];

    if (!resultAccept.IsCompleted) // (1)
        resultAccept.AsyncWaitHandle.WaitOne();

    connectedSocket.BeginReceive(Encoding.UTF8.GetBytes(data), 0, data.Length, SocketFlags.None,
                                 new AsyncCallback(RecvCallback), null);

    // ...
}

When I start receiving, since there's no connection yet, it has to wait for one (1) and blocks the UI (which, again, I don't want).
In addition, when a connection is accepted I get a NullReferenceException because when BeginReceive starts, AcceptCallback is, yes triggered, but has not completed yet so connectedSocket still has to be initialized.

  1. Why is my approach wrong?
  2. Is it possible to achieve what I want with async sockets?
  3. How can I prevent the UI from blocking while waiting for a connection and ensuring the validity of the object?

Upvotes: 1

Views: 846

Answers (1)

usr
usr

Reputation: 171178

  1. Yes, because you are blocking. You need to throw that away.
  2. Yes, there are Socket extension methods on the web that adapt the obsolete APM pattern to the TAP pattern so that you can use await. The exercise becomes trivial then.

Your code should look like:

async Task RunServer() {
 Socket serverSocket = ...;

 while (true) {
  RunConnection(await serverSocket.AcceptAsync());
 }
}

And then provide a Task RunConnectionAsync(Socket s) function that is async as well. Really simple once you leave the obsolete APM pattern alone.

Alternatively, just use threads and synchronous IO. Nothing wrong with doing that. await is a little nicer, though, because it marshals back to the UI thread for you automatically.

Upvotes: 1

Related Questions