0

I'm writing a client application, that has to connect to a server application via TCP socket. The framework of choice is .NET Core 2.0 (it is not ASP.NET Core it is just a console app). I'm using a TcpClient class, and its .BeginConnect() and .EndConnect() methods, to be able to set a connection timeout. Here is the code:

public class Program
{
    public static void Main(String[] args)
    {
        var c = new TcpClient();
        int retryCount = 0;
        var success = false;
        IAsyncResult res;
        do
        {
            if (retryCount > 0) Console.WriteLine("Retry: {0}", retryCount);
            retryCount++;
            c.Close();
            c = new TcpClient();
            res = c.BeginConnect("10.64.4.49", 13000, null, null);
            success = res.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(2));
            Console.WriteLine(success.ToString());
        }
        while (!c.Connected);

        c.EndConnect(res);
        Console.WriteLine("Connected");
        Console.ReadLine();

    }

When I compile, publish and run this Console App, and nothing is listening on the IP address and port, the results if the app is running on Windows or Linux are different. Here are the results on Windows: On Windows 10 machine

Here is what it looks like on Linux: enter image description here

The results are pretty the same, the only difference is on Windows it tries to connect every two seconds, but on Linux, it acts like this two seconds are ignored and goes on a "rampage connection session" as I call it. I'm not sure if this is a .NET Core issue or some Linux tune-up, that Windows already have predefined. Can anyone advice what might be the problem, and eventually propose a solution.

Thanks in advance,

Julian Dimitrov

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
Julian
  • 375
  • 1
  • 8
  • 23
  • Did you make a test to check your assumption about WaitOne and TimeSpan? – PepitoSh Jun 27 '18 at 05:38
  • [WaitOne](https://msdn.microsoft.com/en-us/library/cc190477(v=vs.110).aspx) takes a `TimeSpan` as a timeout value, not as a wait time. You're waiting for the async task (connecting) to complete. Your connection is successful, so it completes as soon as the connection is established. – ProgrammingLlama Jun 27 '18 at 05:41
  • The `WaitOne()' called on a ManualResetEvent on both Win and Linux act the same. Before I use `TimeSpan` I just entered `2000` for the amount of miliseconds - result the same on both OS's – Julian Jun 27 '18 at 05:43
  • @Julian [BeginConnect](https://msdn.microsoft.com/en-us/library/xw3y33he(v=vs.110).aspx) returns an [IAsyncResult](https://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle(v=vs.110).aspx). The documentation for [ManualResetEvent.WaitOne](https://msdn.microsoft.com/en-us/library/cc190477(v=vs.110).aspx) suggests it is a delay, vs [WaitHandle.WaitOne](https://msdn.microsoft.com/en-us/library/cc190477(v=vs.110).aspx) which suggest it is a timeout. – ProgrammingLlama Jun 27 '18 at 05:46
  • The app structure is inspired from the answer of [this](https://stackoverflow.com/questions/17118632/how-to-set-the-timeout-for-a-tcpclient) question. I can't find a way to test `WaitHandle.WaitOne` on the `IAsyncResult` separate from the `.BeginConnect` method. Still I can't find a reasonable answer, why it works on Windows and not on Linux... – Julian Jun 27 '18 at 06:07

1 Answers1

1

I think I understand why you're having an issue, and it seems to be based upon a misunderstanding of what a timeout should do.

For the sake of testing, I changed your code to this:

var sw = Stopwatch.StartNew();
res = c.BeginConnect("127.0.0.1", 12, null, null);
success = res.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10));
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

On Windows, I can see that the connection fails after ~1 second, whereas running the same code within Linux, it fails almost instantly. It seems that Linux is capable of working out if a connection is possible faster than Windows is. I think perhaps you're mistaking the time Windows takes to work out it can't connect with the timeout you've specified.

Next: What is a timeout? A timeout is the maximum time a connection can take to be established. It's a limit. It means that the operation has to complete in less than X seconds (e.g. 10 seconds) or it fails. If an operation completes in 1 second, then it will be immediately returned.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • Another interesting thing I found, when you remove the `success = res.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(10));` line from the example.... it acts the same on Windows ans Linux :) – Julian Jun 27 '18 at 06:50
  • That's because, by removing the wait, you're asynchronously trying to connect, and then not waiting to find out the result. – ProgrammingLlama Jun 27 '18 at 06:51
  • So, no matter what you put in the `.AsyncWaitHandle.WaitOne()`, always on Windows will try to reconnect in about 1 sec, but on Linux almost immediately – Julian Jun 27 '18 at 06:55
  • Julian, It's because you're only defining the maximum time it can take - it's not a delay. You need to use something like `Thread.Sleep` when it fails if you want to insert a delay. – ProgrammingLlama Jun 27 '18 at 06:59
  • 1
    yes, I understood the problem, thanks to your explanation, and the action will be to put `Thread.Sleep()` to insert a delay. Thanks for your answers! – Julian Jun 27 '18 at 07:54