How can I create an HttpListener class on a random port in C#?

微笑、不失礼 提交于 2019-12-17 16:35:33

问题


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?


回答1:


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;
}



回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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




回答6:


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




回答7:


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).


来源:https://stackoverflow.com/questions/223063/how-can-i-create-an-httplistener-class-on-a-random-port-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!