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

发布于 2024-07-07 10:53:45 字数 322 浏览 13 评论 0原文

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

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

本质上,我想要的是:

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

我怎样才能完成这个?

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:

  1. Randomly selected
  2. 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

可遇━不可求 2024-07-14 10:53:45

如果绑定到端口 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;
}

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;
}
别理我 2024-07-14 10:53:45

像这样的事情怎么样:

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

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

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.

泅人 2024-07-14 10:53:45

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

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.

眼眸里的快感 2024-07-14 10:53:45

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

另请记住,当其他进程先于您获取找到的端口时,可能会发生竞争条件。

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.

因为看清所以看轻 2024-07-14 10:53:45

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

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

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).
一场春暖 2024-07-14 10:53:45

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

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

如果没结果 2024-07-14 10:53:45

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

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文