【问题标题】:Find the next TCP port in .NET在 .NET 中查找下一个 TCP 端口
【发布时间】:2010-09-13 09:00:27
【问题描述】:

我想为 WCF 服务调用创建一个新的 net.tcp://localhost:x/Service 端点,并动态分配新的开放 TCP 端口。

我知道当我打开与给定服务器的连接时,TcpClient 将分配一个新的客户端端口。

有没有一种简单的方法可以在 .NET 中找到下一个打开的 TCP 端口?

我需要实际的数字,以便构建上面的字符串。 0 不起作用,因为我需要将该字符串传递给另一个进程,以便我可以在该新通道上回调。

【问题讨论】:

标签: c# .net wcf networking tcp


【解决方案1】:

这就是我要找的东西:

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

【讨论】:

  • 如果另一个进程在您重新打开该端口之前打开了它会发生什么...?
  • 那么你当然会得到一个错误,但这不是我的上下文的问题。
  • 我成功地使用这种技术获得了一个空闲端口。我也担心竞争条件,因为其他一些进程会潜入并抢占最近检测到的空闲端口。所以我写了一个测试,在var port = FreeTcpPort() 之间强制Sleep(100) 并在空闲端口上启动一个HttpListener。然后,我运行了 8 个相同的进程来循环执行此操作。我永远无法达到比赛条件。我的轶事证据(Win 7)是操作系统显然会在短暂端口(几千个)范围内循环,然后再次出现。所以上面的sn-p应该没问题。
  • 如果我理解它是如何正常工作的,你能不能只这样做:TcpListener myActualServer = new TcpListener(ipAddress, 0); myActualServer.Start(); return ((IPEndPoint)myActualServer.LocalEndpoint).Port; 或者 OP 在实际启动他的监听器之前获取端口是否重要?
  • 试图用它来确保我们的构建服务器上的单元测试无论如何都能运行。但是没有。使用这段代码,其中一些甚至在我的本地机器上失败=/
【解决方案2】:

使用端口号 0。TCP 堆栈将分配下一个空闲端口。

【讨论】:

  • 那行不通,因为我需要那个实际的#,而不仅仅是 0。我需要这个数字来构建一个字符串。
  • 这个对我有帮助。如果您通知对方您正在侦听的端口是什么,这实际上确实有效。
  • ServiceHost host = new ServiceHost(typeof(MyService), new Uri("net.tcp://localhost:0/Service"));不上班吗?
  • 是的,它有效。您只需将endpoint.ListenUriMode 设置为Unique。
  • 这对我来说是一个很好的解决方案。使用 System.Net.Sockets.TcpClient.
【解决方案3】:

这是一个与 TheSeeker 接受的答案相当的解决方案。虽然我认为它更具可读性:

using System;
using System.Net;
using System.Net.Sockets;

    private static readonly IPEndPoint DefaultLoopbackEndpoint = new IPEndPoint(IPAddress.Loopback, port: 0);

    public static int GetAvailablePort()
    {
        using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
        {
            socket.Bind(DefaultLoopbackEndpoint);
            return ((IPEndPoint)socket.LocalEndPoint).Port;
        }
    }

【讨论】:

  • 对于 UDP,我使用了以下内容:... SocketType.Dgram, ProtocolType.Udp...
【解决方案4】:

我从 Selenium.WebDriver DLL

中找到了以下代码

命名空间:OpenQA.Selenium.Internal

类:PortUtility

public static int FindFreePort()
{
    int port = 0;
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    try
    {
        IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 0);
        socket.Bind(localEP);
        localEP = (IPEndPoint)socket.LocalEndPoint;
        port = localEP.Port;
    }
    finally
    {
        socket.Close();
    }
    return port;
}

【讨论】:

    【解决方案5】:

    如果您只想提供一个起始端口,并让它返回给您下一个可用的 TCP 端口,请使用如下代码:

    public static int GetAvailablePort(int startingPort)
    {
        var portArray = new List<int>();
    
        var properties = IPGlobalProperties.GetIPGlobalProperties();
    
        // Ignore active connections
        var connections = properties.GetActiveTcpConnections();
        portArray.AddRange(from n in connections
                            where n.LocalEndPoint.Port >= startingPort
                            select n.LocalEndPoint.Port);
    
        // Ignore active tcp listners
        var endPoints = properties.GetActiveTcpListeners();
        portArray.AddRange(from n in endPoints
                            where n.Port >= startingPort
                            select n.Port);
    
        // Ignore active UDP listeners
        endPoints = properties.GetActiveUdpListeners();
        portArray.AddRange(from n in endPoints
                            where n.Port >= startingPort
                            select n.Port);
    
        portArray.Sort();
    
        for (var i = startingPort; i < UInt16.MaxValue; i++)
            if (!portArray.Contains(i))
                return i;
    
        return 0;
    }
    

    【讨论】:

    • 完美运行!
    • 请记住,由于某些限制,这在 Azure 中不起作用。您将收到拒绝访问。使用 netstat 也是如此
    • 谢谢,这行得通。小补充:您可以删除 UDP 检查,因为TCP and UDP ports live in separate namespaces
    【解决方案6】:

    先打开端口,然后给其他进程正确的端口号。

    否则仍有可能其他进程先打开端口,而您仍然有另一个进程。

    【讨论】:

    • 你有代码示例来尽可能简单地获得这样一个 tcp 端口吗?
    • 这就是这样做的方法。一定要小心,如果多个进程打开同一个端口,可能是一种竞争条件。
    【解决方案7】:

    如果您想在给定范围内找到下一个可用的 TCP 端口,这里有一种更简略的实现方式:

    private int GetNextUnusedPort(int min, int max)
    {
        if (max < min)
            throw new ArgumentException("Max cannot be less than min.");
    
        var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
    
        var usedPorts =
            ipProperties.GetActiveTcpConnections()
                .Where(connection => connection.State != TcpState.Closed)
                .Select(connection => connection.LocalEndPoint)
                .Concat(ipProperties.GetActiveTcpListeners())
                .Concat(ipProperties.GetActiveUdpListeners())
                .Select(endpoint => endpoint.Port)
                .ToArray();
    
        var firstUnused =
            Enumerable.Range(min, max - min)
                .Where(port => !usedPorts.Contains(port))
                .Select(port => new int?(port))
                .FirstOrDefault();
    
        if (!firstUnused.HasValue)
            throw new Exception($"All local TCP ports between {min} and {max} are currently in use.");
    
        return firstUnused.Value;
    }
    

    【讨论】:

      【解决方案8】:

      如果您想获得特定范围内的空闲端口以将其用作本地端口/端点:

      private int GetFreePortInRange(int PortStartIndex, int PortEndIndex)
      {
          DevUtils.LogDebugMessage(string.Format("GetFreePortInRange, PortStartIndex: {0} PortEndIndex: {1}", PortStartIndex, PortEndIndex));
          try
          {
              IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
      
              IPEndPoint[] tcpEndPoints = ipGlobalProperties.GetActiveTcpListeners();
              List<int> usedServerTCpPorts = tcpEndPoints.Select(p => p.Port).ToList<int>();
      
              IPEndPoint[] udpEndPoints = ipGlobalProperties.GetActiveUdpListeners();
              List<int> usedServerUdpPorts = udpEndPoints.Select(p => p.Port).ToList<int>();
      
              TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
              List<int> usedPorts = tcpConnInfoArray.Where(p=> p.State != TcpState.Closed).Select(p => p.LocalEndPoint.Port).ToList<int>();
      
              usedPorts.AddRange(usedServerTCpPorts.ToArray());
              usedPorts.AddRange(usedServerUdpPorts.ToArray());
      
              int unusedPort = 0;
      
              for (int port = PortStartIndex; port < PortEndIndex; port++)
              {
                  if (!usedPorts.Contains(port))
                  {
                      unusedPort = port;
                      break;
                  }
              }
              DevUtils.LogDebugMessage(string.Format("Local unused Port:{0}", unusedPort.ToString()));
      
              if (unusedPort == 0)
              {
                  DevUtils.LogErrorMessage("Out of ports");
                  throw new ApplicationException("GetFreePortInRange, Out of ports");
              }
      
              return unusedPort;
          }
          catch (Exception ex)
          {
              string errorMessage = ex.Message;
              DevUtils.LogErrorMessage(errorMessage);
              throw;
          }
      }
      
      
      private int GetLocalFreePort()
      {
          int hemoStartLocalPort = int.Parse(DBConfig.GetField("Site.Config.hemoStartLocalPort"));
          int hemoEndLocalPort = int.Parse(DBConfig.GetField("Site.Config.hemoEndLocalPort"));
          int localPort = GetFreePortInRange(hemoStartLocalPort, hemoEndLocalPort);
          DevUtils.LogDebugMessage(string.Format("Local Free Port:{0}", localPort.ToString()));
          return localPort;
      }
      
      
      public void Connect(string host, int port)
      {
          try
          {
              // Create socket
              Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
              //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
      
              var localPort = GetLocalFreePort();
              // Create an endpoint for the specified IP on any port
              IPEndPoint bindEndPoint = new IPEndPoint(IPAddress.Any, localPort);
      
              // Bind the socket to the endpoint
              socket.Bind(bindEndPoint);
      
              // Connect to host
              socket.Connect(IPAddress.Parse(host), port);
      
              socket.Dispose();
          }
          catch (SocketException ex)
          {
              // Get the error message
              string errorMessage = ex.Message;
              DevUtils.LogErrorMessage(errorMessage);
          }
      }
      
      
      public void Connect2(string host, int port)
      {
          try
          {
              // Create socket
      
              var localPort = GetLocalFreePort();
      
              // Create an endpoint for the specified IP on any port
              IPEndPoint bindEndPoint = new IPEndPoint(IPAddress.Any, localPort);
      
              var client = new TcpClient(bindEndPoint);
              //client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); //will release port when done
      
              // Connect to the host
              client.Connect(IPAddress.Parse(host), port);
      
              client.Close();
          }
          catch (SocketException ex)
          {
              // Get the error message
              string errorMessage = ex.Message;
              DevUtils.LogErrorMessage(errorMessage);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-17
        • 1970-01-01
        • 2014-07-02
        • 1970-01-01
        • 1970-01-01
        • 2012-06-13
        • 2018-12-27
        相关资源
        最近更新 更多