Find the next TCP port in .NET
Asked Answered
O

8

87

I want to create a new net.tcp://localhost:x/Service endpoint for a WCF service call, with a dynamically assigned new open TCP port.

I know that TcpClient will assign a new client side port when I open a connection to a given server.

Is there a simple way to find the next open TCP port in .NET?

I need the actual number, so that I can build the string above. 0 does not work, since I need to pass that string to another process, so that I can call back on that new channel.

Oteliaotero answered 26/9, 2008 at 6:33 Comment(1)
#3563893Vernice
O
154

Here is what I was looking for:

static int FreeTcpPort()
{
  TcpListener l = new TcpListener(IPAddress.Loopback, 0);
  l.Start();
  int port = ((IPEndPoint)l.LocalEndpoint).Port;
  l.Stop();
  return port;
}
Oteliaotero answered 29/9, 2008 at 22:15 Comment(7)
And what happens if another process opens that port before you re-open it...?Janayjanaya
Then you will get an error of course, but that was not an issue for my context.Oteliaotero
I successfully used this technique to get a free port. I too was concerned about race-conditions, with some other process sneaking in and grabbing the recently-detected-as-free port. So I wrote a test with a forced Sleep(100) between var port = FreeTcpPort() and starting an HttpListener on the free port. I then ran 8 identical processes hammering on this in a loop. I could never hit the race condition. My anecdotal evidence (Win 7) is that the OS apparently cycles through the range of ephemeral ports (a few thousand) before coming around again. So the above snippet should be just fine.Rager
If I understand how this works correctly, could you not just do this: TcpListener myActualServer = new TcpListener(ipAddress, 0); myActualServer.Start(); return ((IPEndPoint)myActualServer.LocalEndpoint).Port; or is it important that the OP gets the port before actually starting his listener?Perambulator
Tried to use this to make sure the unit tests on our build server run through, no matter what. But nope. With this piece of code some of them even fail on my local machine =/Iow
This solution is short and hacky --- I absolutely love it!Seidel
One thing to note is that when I tried to use this piece of code, it prioritized ephemeral ports. This isn't necessarily a problem for most use cases, but it was for ours.Defrost
L
34

Use a port number of 0. The TCP stack will allocate the next free one.

Landin answered 26/9, 2008 at 6:35 Comment(5)
That does not work, since I need that actual #, not just 0. I need the number to build a string.Oteliaotero
This one has helped me. This actually does work, if you notify the other side what the port you're listening on is.Audiometer
ServiceHost host = new ServiceHost(typeof(MyService), new Uri("net.tcp://localhost:0/Service")); Is not going to work?Apocalypse
Yes, it works. You just have to set endpoint.ListenUriMode to Unique.Yusuk
This was a good solution for me. Using System.Net.Sockets.TcpClient.Revue
G
21

It's a solution comparable to the accepted answer of TheSeeker. Though I think it's more readable:

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;
        }
    }
Grassquit answered 21/3, 2018 at 13:55 Comment(1)
For UDP I used the following: ... SocketType.Dgram, ProtocolType.Udp...Otherwhere
E
14

I found the following code from Selenium.WebDriver DLL

Namespace: OpenQA.Selenium.Internal

Class: 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;
}
Extraditable answered 18/11, 2019 at 23:37 Comment(0)
I
11

If you just want to give a starting port, and let it return to you the next TCP port available, use code like this:

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;
}
Inelastic answered 29/7, 2017 at 3:37 Comment(3)
Work perfectly!Pathe
keep in mind that this will not work in Azure because of some restrictions. You would receive an Access Denied. The same by using netstatQnp
Thanks, this worked. Small addition: you can remove the UDP checks, because TCP and UDP ports live in separate namespacesConjugated
C
9

First open the port, then give the correct port number to the other process.

Otherwise it is still possible that some other process opens the port first and you still have a different one.

Cartridge answered 26/9, 2008 at 8:0 Comment(2)
Do you have a code sample to get such a tcp port as easy as possible?Oteliaotero
This is the way to do it. Gotta be careful, could be a race condition if more than one process opens the same port.Janayjanaya
S
4

Here's a more abbreviated way to implement this if you want to find the next available TCP port within a given range:

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;
}
Spodumene answered 23/10, 2019 at 16:0 Comment(0)
P
1

If you want to get a free port in a specific range in order to use it as local port / end point:

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);
    }
}
Perfect answered 16/8, 2016 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.