【问题标题】:Thread-safe way to find the next TCP port in .NET (over multiple processes)在 .NET 中查找下一个 TCP 端口的线程安全方法(通过多个进程)
【发布时间】: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


【解决方案1】:

如何构建实用程序以从并行运行的进程中获取空闲 TCP 端口,而不会遇到竞争条件?

你不能。使用一些临时“空闲”端口的想法从一开始就是错误的,因为在具有多个进程的系统上,实际空闲的端口可能会从一个时刻改变到另一个时刻——无论您的应用程序本身是否使用线程。因此,即使您找到了一些现在空闲的端口,它也可能在您成功绑定到它之前已被另一个进程使用。

正确的方法是不要找到空闲端口然后绑定它。正确的方法是将您的侦听器绑定到端口 0,然后确定实际使用的端口。然后直接使用这个已经创建的监听套接字,而不是关闭它并尝试使用它用于一些新套接字的端口。

【讨论】:

  • 感谢您的回答。我感觉合理。我知道在停止侦听器后另一个进程可以使用此端口。由于我们只在测试代码中使用此实用程序,因此如果操作系统以安全的方式循环(增加)端口就足够了。我仍然不明白为什么并非总是如此。 (在我们的测试环境中没有运行其他进程可以窃取空闲端口)但是看起来我们必须找到一个解决方案来确定在将我们的 WCF 服务绑定到端口 0 后使用哪个动态端口,就像你建议的那样。跨度>
  • @JonasBenz:操作系统无需按照您希望的方式处理端口,但实际上会付出更多努力(没有明显的收获)。这种需要只是您的一部分,并且只是因为您的测试是建立在关于操作系统行为的假设之上的,这些假设是不正确的。不要期望操作系统与您的假设保持一致,而是让您的假设与操作系统的行为方式保持一致。
  • 你完全正确。我们可以按照您建议的方式解决我们的问题。
猜你喜欢
  • 2010-09-13
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 1970-01-01
  • 2018-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多