Reputation: 7304
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
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
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