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

后端 未结 7 1455
广开言路
广开言路 2020-12-03 16:34

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 H

相关标签:
7条回答
  • 2020-12-03 17:13

    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

    0 讨论(0)
  • 2020-12-03 17:19

    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;
    }
    
    0 讨论(0)
  • 2020-12-03 17:29

    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).
    0 讨论(0)
  • 2020-12-03 17:31

    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

    0 讨论(0)
  • 2020-12-03 17:33

    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.

    0 讨论(0)
  • 2020-12-03 17:34

    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.

    0 讨论(0)
提交回复
热议问题