我想创建一个在内部为网页提供服务的应用程序,并且可以在同一台机器上的多个实例中运行。为此,我想创建一个HttpListener
侦听以下端口的端口:
- 随机选择
- 目前未使用
本质上,我想要的是:
mListener = new HttpListener();
mListener.Prefixes.Add("http://*:0/");
mListener.Start();
selectedPort = mListener.Port;
我怎样才能做到这一点?
我想创建一个在内部为网页提供服务的应用程序,并且可以在同一台机器上的多个实例中运行。为此,我想创建一个HttpListener
侦听以下端口的端口:
本质上,我想要的是:
mListener = new HttpListener();
mListener.Prefixes.Add("http://*:0/");
mListener.Start();
selectedPort = mListener.Port;
我怎样才能做到这一点?
如果绑定到端口 0,TcpListener 将找到一个随机未使用的端口来监听。
public static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Any, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
像这样的东西怎么样:
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;
}
我不确定您如何找到该机器上正在使用的所有端口,但是如果您尝试侦听已被使用的端口,您应该会遇到异常,在这种情况下,该方法将简单地选择另一个港口。
这是一个来自Snooganz 的答案的答案。它避免了可用性测试和后续绑定之间的竞争条件。
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;
}
在我的机器上,这种方法平均需要 15 毫秒,这对于我的用例来说是可以接受的。希望这对其他人有帮助。
不幸的是,这是不可能的。正如 Richard Dingwall 已经建议的那样,您可以创建一个 TCP 侦听器并使用该端口。这种方法有两个可能的问题:
由于您使用的是 HttpListener(因此使用 TCP 连接),您可以使用对象GetActiveTcpListeners
的方法获取活动 TCP 侦听器的列表IPGlobalProperties
并检查它们的Port
属性。
可能的解决方案可能如下所示:
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;
}
此代码将从startingPort
端口号开始找到第一个未使用的端口并返回true
。如果所有端口都已被占用(这不太可能),则该方法返回false
.
还要记住,当其他进程在您之前占用找到的端口时,可能会发生竞争条件。
我建议尝试Grapevine。它允许您在应用程序中嵌入 REST/HTTP 服务器。它包括一个RestCluster
类,允许您RestServer
在一个地方管理所有实例。
将每个实例设置为使用随机的开放端口号,如下所示:
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);
...
}
入门指南:https ://sukona.github.io/Grapevine/en/getting-started.html
我不相信这是可能的。UriBuilder.Port 的文档指出,“如果未将端口指定为 URI 的一部分,...协议方案的默认端口值将用于连接到主机。”。
请参阅https://msdn.microsoft.com/en-us/library/system.uribuilder.port(v=vs.110).aspx