Reputation:
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:
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?
Upvotes: 42
Views: 29790
Reputation: 310802
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.
Upvotes: 10
Reputation: 7223
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
Upvotes: 1
Reputation: 1564
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.
Upvotes: 2
Reputation: 5696
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:
Upvotes: 1
Reputation: 683
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
Upvotes: -1
Reputation: 314
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.
Upvotes: 19
Reputation: 2742
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;
}
Upvotes: 49