【发布时间】:2019-09-23 13:38:24
【问题描述】:
请注意,您可以在这里找到类似的问题:Find the next TCP port in .NET
我的问题是这个问题的解决方案(接受的答案)不是线程安全的: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;
}
我写了一个测试证明:
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());
}
上面的测试很不稳定。在我的机器上,它失败的次数超过了我运行它的 50%。 以下是失败测试的控制台输出的一部分:
51470
51472
51473
51365
51475
51367
51366
51474
51475
51476
51478
51479
51480
问题是在这种情况下端口51475被返回了两次。请注意,相同的端口是从 2 个并行运行的线程返回的,而不是因为它在端口范围内循环并获得重叠(这发生在循环计数较高的情况下)。
请注意,仅仅为我的用例添加一个 lock 语句是不够的,因为我使用这个动态端口来并行运行系统测试,这些测试在单独的进程中运行。 因此,我正在寻找一种解决方案,它始终为并行运行进程返回一个唯一且免费的 TCP 端口。
如何构建一个实用程序来从并行运行的进程中获取空闲 TCP 端口,而不会遇到竞争条件?
【问题讨论】:
-
除非这是某种编码实践,否则我建议您:a) 让操作系统选择其端口,b) 如果 A 不可行,则解析
netstat -a -n -p tcp输出以获取使用的端口。 -
如Find the next TCP port in .NET 接受的答案所示,正确的方法是让操作系统使用端口 0 选择一个端口。在多进程系统上,其他一切都容易出现竞争条件,无论是否你是否使用线程。即使您找到了一个看似未使用的端口(使用 bradbury9 建议的 netstat),绑定也可能会失败,因为另一个进程也发现了该端口并在您的进程执行之前绑定到该端口。
-
好吧,你立即
Stop()你的监听器,使端口再次可用,为什么操作系统要避免再次使用该端口? -
@SteffenUllrich OP 在他问题的第一句话中将possible duplicate 称为“类似问题”。他还包括了该问题的公认答案,并解释了为什么该答案不适合他的需求(他声称它不是线程安全的)。
-
除非您主动绑定到打开的端口并使用它,否则除您的应用程序线程之外的任何其他进程都可能使用它,您无法阻止它。您应该没有有
static int FreeTcpPort(),而是static TcpListener Connect(),并且只需读取打开的端口号。否则机器中的其他进程可能会打开它,您无法阻止进程之外的竞争条件。
标签: c# multithreading networking tcp concurrency