Mario Vernari
Mario Vernari

Reputation: 7304

Hang-up on async TCP client connection

I'm taking practice with the async CTP framework, and as exercise I would create a TCP client able to query a server (using an arbitrary protocol). Anyway, I'm stuck at the very early stage because an issue on the connection. Either I still didn't understand some basic point, or there is something strange.

So, here is the async connector:

public class TaskClient
{
    public static Task<TcpClient> Connect(IPEndPoint endPoint)
    {
        //create a tcp client
        var client = new TcpClient(AddressFamily.InterNetwork);

        //define a function to return the client
        Func<IAsyncResult, TcpClient> em = iar =>
        {
            var c = (TcpClient)iar.AsyncState;
            c.EndConnect(iar);
            return c;
        };

        //create a task to connect the end-point async
        var t = Task<TcpClient>.Factory.FromAsync(
            client.BeginConnect,
            em,
            endPoint.Address.ToString(),
            endPoint.Port,
            client);

        return t;
    }
}

I mean to call this function only once, then having back a TcpClient instance to use for any succeeding query (code not shown here).

Somewhere in my form, I call the function above as follows:

    //this method runs on the UI thread, so can't block
    private void TryConnect()
    {
        //create the end-point
        var ep = new IPEndPoint(
            IPAddress.Parse("192.168.14.112"), //this is not reachable: correct!
            1601);

        var t = TaskClient
            .Connect(ep)
            .ContinueWith<TcpClient>(_ =>
            {
                //tell me what's up
                if (_.IsFaulted)
                    Console.WriteLine(_.Exception);
                else
                    Console.WriteLine(_.Result.Connected);

                return _.Result;
            })
            .ContinueWith(_ => _.Result.Close());

        Console.WriteLine("connection in progress...");

        //wait for 2" then abort the connection
        //Thread.Sleep(2000);
        //t.Result.Client.Close();
    }

The test is to try to connect a remote server, but it has to be unreachable (PC on, but service stopped).

When I run the TryConnect function, it returns correctly "connection in progress..." as soon, then displays an exception because the remote endpoint is off. Excellent!

The problem is that it needs several seconds to return the exception, and I would like to give the chance to the user to cancel the operation in progress. According to the MSDN specs about the BeginConnect method, if you wish to abort the async operation, just call Close on the working socket.

So, I tried to add a couple of lines at the end (commented out as above), so to simulate the users cancellation after 2 seconds. The result looks as a hang of the app (hourglass). By pausing the IDE, it stops on the very last line t.Result.Client.Close(). However, by stopping the IDE everything closes normally, without any exception.

I've also tried to close the client directly as t.Result.Close(), but it's exactly the same.

It's me, or there's anything broken on the connection process?

Thanks a lot in advance.

Upvotes: 3

Views: 3340

Answers (2)

Softlion
Softlion

Reputation: 12585

t.Result.Close() will wait for the t task completion. t.ContinueWith() will also wait for the completion of the task.

To cancel you must wait on 2 tasks: the tcp and a timer.
Using the async tcp syntax:

await Task.WhenAny(t,Task.Delay(QueryTimeout));
if (!t.IsCompleted)
    tcpClient.Close(); //Cancel task

Upvotes: 2

Jonathan Dickinson
Jonathan Dickinson

Reputation: 9218

Try calling Dispose() on the object - it's a bit more agressive than Close(). You could look at the various Timeout members on the TcpClient class and set them to more appropriate values (e.g. 1 second in a LAN environment is probably good enough). You can also have a look at the CancellationTokenSource functionality in .Net 4.0. This allows you to signal to a Task that you wish it to discontinue - I found an article that might get you started.

You should also find out which thread is actually stalling (the primary thread might just be waiting for another thread that is stalled), e.g. the .ContinueWith(_ => _.Result.Close()) might be the problem (you should check what the behaviour is when closing a socket twice). While debugging open the Threads window (Debug -> Windows -> Threads) and have a look through each thread.

Upvotes: 1

Related Questions