32

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

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

本质上,我想要的是:

mListener = new HttpListener();
mListener.Prefixes.Add("http://*:0/");
mListener.Start();
selectedPort = mListener.Port;

我怎样才能做到这一点?

4

7 回答 7

41

如果绑定到端口 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;
}
于 2010-10-20T12:55:46.973 回答
16

像这样的东西怎么样:

    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;
    }

我不确定您如何找到该机器上正在使用的所有端口,但是如果您尝试侦听已被使用的端口,您应该会遇到异常,在这种情况下,该方法将简单地选择另一个港口。

于 2008-10-21T19:38:45.603 回答
7

这是一个来自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 毫秒,这对于我的用例来说是可以接受的。希望这对其他人有帮助。

于 2017-10-10T12:06:37.903 回答
1

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

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

由于您使用的是 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.

还要记住,当其他进程在您之前占用找到的端口时,可能会发生竞争条件。

于 2016-09-29T14:20:33.197 回答
1

我建议尝试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

于 2017-04-25T17:55:51.380 回答
0

我不相信这是可能的。UriBuilder.Port 的文档指出,“如果未将端口指定为 URI 的一部分,...协议方案的默认端口值将用于连接到主机。”。

请参阅https://msdn.microsoft.com/en-us/library/system.uribuilder.port(v=vs.110).aspx

于 2015-10-15T20:28:52.980 回答