Как я могу создать класс HttpListener на случайном порту в C #? - PullRequest
27 голосов
/ 21 октября 2008

Я хотел бы создать приложение, которое обслуживает веб-страницы внутри и может запускаться в нескольких экземплярах на одном компьютере. Для этого я хотел бы создать HttpListener, который прослушивает порт:

  1. Случайно выбранный
  2. В настоящее время не используется

По сути, я хотел бы что-то вроде:

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

Как мне это сделать?

Ответы [ 7 ]

39 голосов
/ 20 октября 2010

TcpListener найдет случайный неиспользуемый порт для прослушивания, если вы подключитесь к порту 0.

public static int GetRandomUnusedPort()
{
    var listener = new TcpListener(IPAddress.Any, 0);
    listener.Start();
    var port = ((IPEndPoint)listener.LocalEndpoint).Port;
    listener.Stop();
    return port;
}
15 голосов
/ 21 октября 2008

Как насчет этого:

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

Я не уверен, как вы найдете все порты, которые используются на этой машине, но вы должны получить исключение, если вы попытаетесь прослушивать порт, который уже используется, и в этом случае метод будет просто выберите другой порт.

2 голосов
/ 10 октября 2017

Вот ответ, полученный из ответа Снуганца . Это позволяет избежать состояния гонки между проверкой доступности и последующим связыванием.

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 мс, что приемлемо для моего варианта использования. Надеюсь, это поможет кому-то еще.

1 голос
/ 25 апреля 2017

Я бы рекомендовал попробовать Виноградная лоза . Это позволяет встраивать сервер 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

1 голос
/ 29 сентября 2016

Поскольку вы используете HttpListener (и, следовательно, TCP-соединения), вы можете получить список активных прослушивателей TCP с помощью метода GetActiveTcpListeners объекта 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.

Также помните о возможности состояния гонки, которое может произойти, если какой-либо другой процесс захватит найденный порт раньше вас.

0 голосов
/ 08 июля 2016

К сожалению, это невозможно. Как уже предположил Ричард Дингволл, вы можете создать прослушиватель TCP и использовать этот порт. Этот подход имеет две возможные проблемы:

  • Теоретически может возникнуть состояние гонки, потому что другой слушатель TCP может использовать этот порт после закрытия.
  • Если вы не являетесь администратором, вам нужно выделить префиксы и комбинации портов, чтобы разрешить привязку к нему. Это невозможно сделать, если у вас нет прав администратора. По сути это означает, что вам необходимо запустить HTTP-сервер с правами администратора (что, как правило, является плохой идеей).
0 голосов
/ 15 октября 2015

Я не верю, что это возможно. В документации для UriBuilder.Port говорится: «Если порт не указан как часть URI, ... для подключения к хосту будет использоваться значение порта по умолчанию для схемы протокола.».

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...