2

Trying to implement a timeout parameter for connecting to a server but I'm not having much luck. Here's my code:

client = new TcpClient();

Task task = Task.Factory.FromAsync(client.BeginConnect, client.EndConnect, host, port, null);

bool taskCompleted = connectTask.Wait(timeoutInMS);

if (taskCompleted)
{
    // Do something with the establishment of a successful connection
}
else
{
    Console.WriteLine("Timeout!");
}

Unfortunately if timeoutInMS is greater than 1022, an AggregateException is thrown on this line:

bool taskCompleted = connectTask.Wait(timeoutInMS);

Adjusting the timeout properties of the TcpClient didn't seem to make any differences.

Taryn
  • 242,637
  • 56
  • 362
  • 405
Ryan Peschel
  • 11,087
  • 19
  • 74
  • 136

1 Answers1

3

Most probably because the Task has not produced a result yet in 1022 ms. But waiting for a bit more than that, the task was able to capture the SocketException thrown by TcpClient.

Your situation is analogous to the following:

var task = Task.Factory.StartNew(() =>
{
  Thread.Sleep(5000);
  throw new Exception();
});

bool taskCompleted = task.Wait(4000); // No exception
bool taskCompleted = task.Wait(6000); // Exception

By the way, why are you using FromAsync() when you are using TcpClient in a syncrhonous manner?

Ilian
  • 5,113
  • 1
  • 32
  • 41
  • The example is a bit contrived. In my actual code at the moment I am not calling Wait and after the call to FromAsync I call `task.ContinueWith(...);`. Do you know of a solution to allow the implementation of custom timeouts with Tasks? – Ryan Peschel Oct 12 '11 at 04:42
  • @Ryan, [this question](http://stackoverflow.com/q/4238345/464709) might be of interest to you then. – Frédéric Hamidi Oct 12 '11 at 04:49
  • I don't know of an elegant way to do that in TPL. But I think what is typically implemented is to record the time you made the request (maybe `StopWatch`?), then get the elapsed time in your task continuation. If the elapsed time is greater than your timeout, then don't proceed with the request and maybe close the `NetworkStream` if required. – Ilian Oct 12 '11 at 04:51
  • 1
    @FrédéricHamidi That solution probably works. But be aware that it will block a ThreadPool thread. One loses the advantage of using BeginConnect/EndConnect which waits on I/O completion ports. – Ilian Oct 12 '11 at 04:56
  • 1
    @IlianPinzon: What do you think of the second reply? Create one work thread and one timeout thread, start them both, and then use `Task.WaitAny` to find out which finished first. My only concern with that is that it basically doubles the number of thread pool threads for incoming connections (not sure if this is premature optimization. What do you think?) – Ryan Peschel Oct 12 '11 at 05:00
  • I believe you can't pass a `CancellationToken` to a `FromAsync` task. So even though your timeout task fired first, your `BeginConnect` task can still complete after. You will still have to handle the timeout condition manually in your task continuation. – Ilian Oct 12 '11 at 05:06