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:
- Randomly selected
- 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?
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;
}
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.
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.
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.
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
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
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