40

I would like to create an application that serves web pages internally and can be run in multiple instances on the same machine. To do so, I would like to create an HttpListener that listens on a port that is:

  1. Randomly selected
  2. Currently unused

Essentially, what I would like is something like:

mListener = new HttpListener();
mListener.Prefixes.Add("http://*:0/");
mListener.Start();
selectedPort = mListener.Port;

How can I accomplish this?

gturri
  • 13,807
  • 9
  • 40
  • 57

7 Answers7

46

TcpListener will find a random un-used port to listen on if you bind to port 0.

public static int GetRandomUnusedPort()
{
    var listener = new TcpListener(IPAddress.Any, 0);
    listener.Start();
    var port = ((IPEndPoint)listener.LocalEndpoint).Port;
    listener.Stop();
    return port;
}
Richard Dingwall
  • 2,692
  • 1
  • 31
  • 32
  • 1
    Does this take into account firewalls, if the port is blocked? – CleverPatrick May 31 '12 at 14:33
  • 2
    No, there is no way for it to know that. – Richard Dingwall Jun 10 '12 at 11:51
  • 11
    This doesn't answer the question about the HttpListener class. – jnm2 Jun 29 '13 at 17:29
  • @jnm2 This answer provides a way to find a random unused port. Creating the HttpListener on that port is then trivial. – piedar Feb 27 '15 at 23:27
  • 11
    I would say fair enough except for the race condition between releasing the port at the end of your method and starting the HttpListener. – jnm2 Feb 27 '15 at 23:55
  • 6
    Just for your information, Google uses your code in all their OAuth codes and examples :) https://github.com/googlesamples/oauth-apps-for-windows/blob/a9c06c539adc21eab752537b234219c448356001/OAuthDesktopApp/OAuthDesktopApp/MainWindow.xaml.cs#L47 ) – frzsombor Aug 30 '17 at 01:45
  • 2
    Race condition makes this option unusable for me. Too many intermittent errors. – Ken Lyon Oct 29 '20 at 19:09
18

How about something like this:

    static List<int> usedPorts = new List<int>();
    static Random r = new Random();

    public HttpListener CreateNewListener()
    {
        HttpListener mListener;
        int newPort = -1;
        while (true)
        {
            mListener = new HttpListener();
            newPort = r.Next(49152, 65535); // IANA suggests the range 49152 to 65535 for dynamic or private ports.
            if (usedPorts.Contains(newPort))
            {
                continue;
            }
            mListener.Prefixes.Add(string.Format("http://*:{0}/", newPort));
            try
            {
                mListener.Start();
            }
            catch
            {
                continue;
            }
            usedPorts.Add(newPort);
            break;
        }

        return mListener;
    }

I'm not sure how you would find all of the ports that are in use on that machine, but you should get an exception if you try to listen on a port that is already being used, in which case the method will simply pick another port.

lubos hasko
  • 24,752
  • 10
  • 56
  • 61
Snooganz
  • 304
  • 1
  • 3
  • 2
    You may want to consider using the `MinPort`/`MaxPort` constants instead, [MSDN link](http://msdn.microsoft.com/en-us/library/vstudio/system.net.ipendpoint_fields(v=vs.110).aspx) – Joseph Lennox May 13 '14 at 16:02
  • @JosephLennox, the `MinPort` constant is zero. The minimum value given in the answer is more suitable. – Drew Noakes Oct 10 '17 at 11:40
  • @Snooganz, the catch block should probably dispose `mListener`. Also, `usedPorts` chould be a `Set` for linear membership test. Personally I'd scope `usedPorts` within the method (or get rid of it altogether) as if you used this in a very long-running system you could eventually run out of ports, making the `while` loop run forever, even if ports become available over time. – Drew Noakes Oct 10 '17 at 11:47
8

Here's an answer, derived from Snooganz's answer. It avoids the race condition between testing for availability, and later binding.

public static bool TryBindListenerOnFreePort(out HttpListener httpListener, out int port)
{
    // IANA suggested range for dynamic or private ports
    const int MinPort = 49215;
    const int MaxPort = 65535;

    for (port = MinPort; port < MaxPort; port++)
    {
        httpListener = new HttpListener();
        httpListener.Prefixes.Add($"http://localhost:{port}/");
        try
        {
            httpListener.Start();
            return true;
        }
        catch
        {
            // nothing to do here -- the listener disposes itself when Start throws
        }
    }

    port = 0;
    httpListener = null;
    return false;
}

On my machine this method takes 15ms on average, which is acceptable for my use case. Hope this helps someone else.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • 1
    This is perfect, thank you. It's the only approach that is truly reliable, as there's no latency between finding a port and using it, and no attempts to try to manually keep a list of ports that are in use. I'm going to use this to make my tests more reliable. – Ken Lyon Oct 29 '20 at 19:15
  • I just thought of one minor improvement here, actually. Iterating through the ports in order is more likely to have a collision since every caller starts at the same value. However, picking a random value in that range every time might give more consistent success. It's a minor point, though. I still love that it's atomic. :) – Ken Lyon Oct 29 '20 at 19:29
  • @KenLyon the downside of naively selecting a port randomly is that if none are available, the method will never return. Instead you could keep track of which have been checked, but this will require reserving memory for the 16,320 possible ports. Using one bit per port would take 2,040 bytes which is more than I'd usually like to reserve on the stack. – Drew Noakes Nov 08 '20 at 22:32
  • It's true, that would be the most comprehensive solution. In my case, the problem was quite rare to begin with. It happened during unit tests that were part of our build. I don't want to slow down the tests, and I don't expect our test server to run out of ports. As a compromise, I try up to 50 random ports then fail after that. I hope this will give me the balance between performance and reliability. – Ken Lyon Nov 10 '20 at 00:12
2

Since you are using an HttpListener (and therefore TCP connections) you can get a list of active TCP listeners using GetActiveTcpListeners method of the IPGlobalProperties object and inspect their Port property.

The possible solution may look like this:

private static bool TryGetUnusedPort(int startingPort, ref int port)
{
    var listeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();

    for (var i = startingPort; i <= 65535; i++)
    {
        if (listeners.Any(x => x.Port == i)) continue;
        port = i;
        return true;
    }

    return false;
}

This code will find first unused port beginning from the startingPort port number and return true. In case all ports are already occupied (which is very unlikely) the method returns false.

Also keep in mind the possibility of a race condition that may happen when some other process takes the found port before you do.

dodbrian
  • 1,394
  • 14
  • 18
  • Interesting. That method took about 130ms on my machine (for the first call) which might be unacceptable in some scenarios. There's still a race condition here, so calling code needs to defend against that. Also, why use `ref` and not `out`? – Drew Noakes Oct 10 '17 at 11:35
  • maybe he doesn't like out. In any case i'm upvoting this, because this is quite useful for identifying an available port in .net core before starting the web server. Core will stomp on the existing server if i have one running (example: The settings page from XScheduler) and this will allow me to prevent it. – John Lord Apr 09 '23 at 04:22
  • Man, that was long ago... There's definitely no need to `ref` unless you want it to pass the starting value as well and get rid of the first argument. – dodbrian Apr 09 '23 at 09:00
1

Unfortunately, this isn't possible. As Richard Dingwall already suggested, you could create a TCP listener and use that port. This approach has two possible problems:

  • In theory a race condition may occur, because another TCP listener might use this port after closing it.
  • If you're not running as an Administrator, then you need to allocate prefixes and port combinations to allow binding to it. This is impossible to manage if you don't have administrator privileges. Essentially this would impose that you need to run your HTTP server with administrator privileges (which is generally a bad idea).
Ramon de Klein
  • 5,172
  • 2
  • 41
  • 64
  • I have experienced the race condition first hand. In my case, we have several tests running concurrently, all using a TcpListener which is stopped prior to creating an HttpListener. Every so often there's an error because the port is already in use. BTW +1 for actually answering the question. :) – Ken Lyon Oct 29 '20 at 19:12
1

I'd recommend trying Grapevine. It allows you embed a REST/HTTP server in your application. It includes a RestCluster class that will allow you to manage all of your RestServer instances in a single place.

Set each instance to use a random, open port number like this:

using (var server = new RestServer())
{
    // Grab the next open port (starts at 1)
    server.Port = PortFinder.FindNextLocalOpenPort();

    // Grab the next open port after 2000 (inclusive)
    server.Port = PortFinder.FindNextLocalOpenPort(2000);

    // Grab the next open port between 2000 and 5000 (inclusive)
    server.Port = PortFinder.FindNextLocalOpenPort(200, 5000);
    ...
}

Getting started guide: https://sukona.github.io/Grapevine/en/getting-started.html

Scott Offen
  • 6,933
  • 3
  • 21
  • 24
0

I do not believe this is possible. The documentation for UriBuilder.Port states, "If a port is not specified as part of the URI, ... the default port value for the protocol scheme will be used to connect to the host.".

See https://msdn.microsoft.com/en-us/library/system.uribuilder.port(v=vs.110).aspx

Andrew Rondeau
  • 667
  • 7
  • 18