4

Note that you can find a similar question here: Find the next TCP port in .NET

My problem is that the solution (accepted answer) of this question is not thread-safe: Find the next TCP port in .NET - Top answer

static int FreeTcpPort()
{
    TcpListener l = new TcpListener(IPAddress.Loopback, 0);
    l.Start();
    int port = ((IPEndPoint)l.LocalEndpoint).Port;
    l.Stop();
    return port;
}

I wrote a test which proves that:

private readonly BlockingCollection<int> _freePorts = new BlockingCollection<int>();

[Test]
public void FreeTcpPort_ShouldBeThreadSafe()
{
    // Act
    Parallel.For(0, 10000,
        index =>
        {
            int freeTcpPort = FreeTcpPort();
            _freePorts.Add(freeTcpPort);
            Console.WriteLine(freeTcpPort);
        } );

    var query = _freePorts.GroupBy(x => x)
        .Where(g => g.Count() > 1)
        .Select(y => new { Element = y.Key, Counter = y.Count() })
        .ToList();

    // Assert
    Assert.That(query.All(x => x.Counter == 1), () => query.First(x => x.Counter > 1).ToString());
}

The test above is flaky. On my machine it fails more then 50% of the times I run it. Here is a part of the console output of a failing test:

51470
51472
51473
51365
51475
51367
51366
51474
51475
51476
51478
51479
51480

The problem is that port 51475 in this case is returned twice. Note that the same port is returned from 2 parallel running threads and not because it cycles through the port range and get an overlap (which happens with a higher loop count).

Note that it is not enough to just add a lock statement for my use case, because I use this dynamic ports for parallel running system tests, which run in separate processes. Therefore I am searching for a solution which always returns a unique and free TCP port also for parallel running process.

How can I build a utility to get a free TCP port from parallel running processes, without running into race conditions?

Jonas Benz
  • 503
  • 3
  • 12
  • Unless this is some sort of coding practice, I would suggest you: a) Leave the operative system choose its ports and b) Parse `netstat -a -n -p tcp` output to get the used ports if A is not possible. – Cleptus Sep 23 '19 at 13:43
  • As shown in the accepted answer to [Find the next TCP port in .NET](https://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net) the proper way is to let the OS pick a port by using port 0. Everything else is prone to race conditions on multi-process systems, no matter if you use threading or not. And even if you've found a seemingly unused port (with netstat as suggested by bradbury9) the binding might fail since another process found this port too and bound to it before your process could do it. – Steffen Ullrich Sep 23 '19 at 13:47
  • Well, you instantly `Stop()` your listener, making the Port available again, why should the OS refrain from using that port again? – Freggar Sep 23 '19 at 13:53
  • 1
    @SteffenUllrich the OP referred to the [possible duplicate](https://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net) as "similar question" in the very first sentence of his question. He also included the accepted answer of that question, and explained why that answer is unsuitable for his needs (he claims that it is not thread-safe). – Theodor Zoulias Sep 23 '19 at 13:53
  • *"(it is not thread-safe)"* - this is your claim only and I very much doubt it. The OS not only picks a port but also directly binds to it, so it is impossible for another process to bind to the same port. – Steffen Ullrich Sep 23 '19 at 13:54
  • @SteffenUllrich I DO use the accepted answer from the question you linked to. Still there seams to be some kind of race condition. – Jonas Benz Sep 23 '19 at 13:54
  • @JonasBenz: Of course you must keep the socket open (i.e. no `l.Stop()`) in order to keep the port used. For what other reason than to have a listener socket (which you directly get by using port 0) you need the next port anyway? – Steffen Ullrich Sep 23 '19 at 13:56
  • @SteffenUllrich The accpted answer from https://stackoverflow.com/a/150974/3446784 does closes the listener, which works in 99.9% of the cases, because the OS cycles through the port range. But it looks like there is some kind of race-condition, where it doesn't cycle properly. – Jonas Benz Sep 23 '19 at 14:00
  • 1
    Unless you actively bind to the opened port and use it, any other process apart from your application threads could potentially use it, and you could not prevent it. You should **not** have a `static int FreeTcpPort()` but `static TcpListener Connect()` and just read the opened port number. Otherwise other processes in the machine could potentially open it, you cannot prevent a race condition outside your process. – Cleptus Sep 23 '19 at 14:04
  • @bradbury9 I know, but as we only use that utility in our test environment, where there are no other processes then the parallel running tests, which could steal the free port, it would have been good enough if it would increment the ports in a thread safe way. – Jonas Benz Sep 23 '19 at 14:38

1 Answers1

3

How can I build a utility to get a free TCP port from parallel running processes, without running into race conditions?

You can't. The idea of using some temporarily "free" port is wrong from start since what is actually free can change from one moment to the other on a system with multiple processes - no matter if your application itself uses threads or not. Thus, even if you've found some port which is free now it might have been used by another process before you've managed to bind to it.

The proper way is not to find a free port and then bind to it. The proper way is instead to bind your listener to port 0 and then determine which port was actually used. Then use this already created listener socket directly instead of closing it and trying to use the port it used for some new socket.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks for your answer. Makes sense to me. I am aware that another process could use this port after stopping the listener. As we only use this utility in test code it would have been good enough, if the OS would cylce (increment) the ports in a tread safe way. I still doesn't understand why this is not always the case. (There are no other processes running on our test environment which could steal a free port) But it seams like we have to find a solution which determines which dynamic port was used after binding our WCF service to port 0, like you suggest. – Jonas Benz Sep 23 '19 at 14:34
  • 1
    @JonasBenz: There is no need to for the OS to handle the ports in the way you want it to but it would actually more effort (for no obvious gain). This need is only on your part and only because your tests are build on assumptions about the OS behavior which are not true. Don't expect the OS to align itself with your assumptions but align your assumptions with how the OS behaves instead. – Steffen Ullrich Sep 23 '19 at 15:08
  • You are totally right. We could solve our problem in the way you suggested. – Jonas Benz Sep 24 '19 at 13:02