如何在C#中的随机端口上创建HttpListener类?

我想创建一个在内部提供Web页面的应用程序,并且可以在同一台机器上的多个实例中运行。 为此,我想创建一个侦听端口的HttpListener

  1. 随机选择
  2. 目前尚未使用

基本上,我想要的是:

 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 usedPorts = new List(); 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; } 

在我的机器上,这种方法平均需要15ms,这对我的用例是可以接受的。 希望这有助于其他人。

由于您使用的是HttpListener(以及TCP连接),因此可以使用IPGlobalProperties对象的GetActiveTcpListeners方法获取活动TCP侦听器的列表,并检查其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

不幸的是,这是不可能的。 正如Richard Dingwall已经建议的那样,您可以创建一个TCP侦听器并使用该端口。 这种方法有两个可能的问题:

  • 理论上可能会出现竞争条件,因为另一个TCP侦听器在关闭它之后可能会使用此端口。
  • 如果您不是以管理员身份运行,则需要分配前缀和端口组合以允许绑定它。 如果您没有管理员权限,则无法管理。 从本质上讲,这将强制您需要以管理员权限运行您的HTTP服务器(这通常是一个坏主意)。