问题
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?
回答1:
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.
来源:https://stackoverflow.com/questions/58063770/thread-safe-way-to-find-the-next-tcp-port-in-net-over-multiple-processes