Найти сервер, прослушивающий определенный порт в локальной сети - PullRequest
7 голосов
/ 02 сентября 2011

У меня есть серверное приложение. У меня также есть клиентское приложение. Я могу установить TCP-соединение между приложениями, когда оба приложения находятся в одной сети. давайте предположим, что компьютер, на котором запущено серверное приложение, прослушивает новые подключения через порт 2121 и имеет IP-адрес локальной сети 192.168.0.120. На другом компьютере с клиентским приложением я смогу установить соединение, указав номер порта 2121 и IP-адрес 192.168.0.120.

Есть ли способ найти все компьютеры в сети, которые прослушивают порт 2121?

Один алгоритм, о котором я сейчас думаю, выглядит так:

  • получить IP-адрес текущего компьютера и предположить, что он имеет вид 192.168.0.145.

  • теперь, скорее всего, сервер будет прослушивать IP-адреса 192.168.0.

  • , затем пропингуйте 192.168.0.1 на порт 2121, затем 192.168.0.2 на порт 2121 ... и продолжайте.

Я не знаю, эффективен ли этот метод. более того, возможно, что сервер прослушивает IP-адрес 192.168.1.x

Итак, какие изменения я должен внести в свое серверное и клиентское приложение, чтобы клиент смог найти все серверы, прослушивающие порт 2121?

Ответы [ 7 ]

5 голосов
/ 11 сентября 2011

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

Обычно возможный диапазон IP-адресов - тот, который задается маской подсети (http://en.wikipedia.org/wiki/Subnetwork). Точнее говоря, часть IP, которая изменяется, это та часть, когда в маске подсети у вас есть 0 бит (всегда в конце маски).

В вашем примере:

  • если маска 255.255.255.0, то ваш возможный диапазон IP-адресов 192.168.0. *.
  • если IP также может быть 192.168. 1 . * Тогда, вероятно, маска должна быть 255.255. 0 .0
  • Вы также можете иметь маску, например 255.255.255.128, и диапазон будет 192.18.1. [1-126]. Вы можете практически узнать больше, используя http://www.subnet -calculator.com /

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

  • в вашей сети больше DHCP-серверов, что очень плохо, так как у вас будут "условия гонки". Решение здесь состоит в том, чтобы исправить вашу инфраструктуру, удалив все, кроме 1 DHCP-сервера
  • вы вручную установили IP-адреса (возможно, на ноутбуках). Решение состоит в том, чтобы перейти на DHCP (если вам нужен определенный IP, который всегда будет назначаться определенному компьютеру, используйте статический DHCP)

Возвращаясь к проблеме обнаружения проблемы проверки того, что «что-то» прослушивает определенный порт, протокол ICMP здесь не самый лучший, поскольку большинство брандмауэров фильтруют как широковещательный ICMP, так и один ICMP. Если мы действительно говорим о сервере, скорее всего, вам пришлось вручную открыть порт, который вы ищете. Кроме того, , даже если все компьютеры отвечают, вы по-прежнему не знаете, если они размещают ваш хотел сервис .


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

В этой реализации я тестирую последовательно, что оказывается очень медленным, так как время ожидания соединения составляет 30 секунд, если хост не включен. Для нескольких сотен кандидатов это звучит не слишком хорошо. Однако, если большинство хостов доступно (даже если они не размещают ваш сервис), все будет работать в несколько раз быстрее.

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

Также, вы могли бы попробовать пинговать (ICMP) раньше, но вы могли пропустить действительные серверы .


    static void Main(string[] args)
    {

        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        int wantedPort = 21;    //this is the port you want

        byte[] msg = Encoding.ASCII.GetBytes("type msg here");


        foreach (NetworkInterface netwIntrf in NetworkInterface.GetAllNetworkInterfaces())
        {

            Console.WriteLine("Interface name: " + netwIntrf.Name);

            Console.WriteLine("Inteface working: {0}", netwIntrf.OperationalStatus == OperationalStatus.Up);

            //if the current interface doesn't have an IP, skip it
            if (! (netwIntrf.GetIPProperties().GatewayAddresses.Count > 0))
            {
                break;
            }

            //Console.WriteLine("IP Address(es):");

            //get current IP Address(es)
            foreach (UnicastIPAddressInformation uniIpInfo in netwIntrf.GetIPProperties().UnicastAddresses)
            {
                //get the subnet mask and the IP address as bytes
                byte[] subnetMask = uniIpInfo.IPv4Mask.GetAddressBytes();
                byte[] ipAddr = uniIpInfo.Address.GetAddressBytes();

                // we reverse the byte-array if we are dealing with littl endian.
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(subnetMask);
                    Array.Reverse(ipAddr);
                }

                //we convert the subnet mask as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
                uint maskAsInt = BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t subnet={0}", Convert.ToString(maskAsInt, 2));

                //we convert the ip addres as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
                uint ipAsInt = BitConverter.ToUInt32(ipAddr, 0);
                //Console.WriteLine("\t ip={0}", Convert.ToString(ipAsInt, 2));

                //we negate the subnet to determine the maximum number of host possible in this subnet
                uint validHostsEndingMax = ~BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t !subnet={0}", Convert.ToString(validHostsEndingMax, 2));

                //we convert the start of the ip addres as uint (the part that is fixed wrt the subnet mask - from here we calculate each new address by incrementing with 1 and converting to byte[] afterwards 
                uint validHostsStart = BitConverter.ToUInt32(ipAddr, 0) & BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t IP & subnet={0}", Convert.ToString(validHostsStart, 2));

                //we increment the startIp to the number of maximum valid hosts in this subnet and for each we check the intended port (refactoring needed)
                for (uint i = 1; i <= validHostsEndingMax; i++)
                {
                    uint host = validHostsStart + i;
                    //byte[] hostAsBytes = BitConverter.GetBytes(host);
                    byte[] hostBytes = BitConverter.GetBytes(host);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(hostBytes);
                    }

                    //this is the candidate IP address in "readable format" 
                    String ipCandidate = Convert.ToString(hostBytes[0]) + "." + Convert.ToString(hostBytes[1]) + "." + Convert.ToString(hostBytes[2]) + "." + Convert.ToString(hostBytes[3]);
                    Console.WriteLine("Trying: " + ipCandidate);


                    try
                    {
                        //try to connect
                        sock.Connect(ipCandidate, wantedPort);
                        if (sock.Connected == true)  // if succesful => something is listening on this port
                        {
                            Console.WriteLine("\tIt worked at " + ipCandidate);
                            sock.Close();
                            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        }
                        //else -. goes to exception
                     }
                    catch (SocketException ex)
                    { 
                        //TODO: if you want, do smth here
                        Console.WriteLine("\tDIDN'T work at " + ipCandidate);
                    }
                }
            }
            Console.ReadLine();
        }
        sock.Close();
    }
3 голосов
/ 13 сентября 2011

(извините за мой плохой английский) Мне действительно нужно что-то похожее на это и только что узнал о многоадресной рассылке. Здесь вы можете найти статью и пример. Пример приложения из статьи отлично работал на моей локальной сети. Я не знаю точно, как это работает, но, возможно, вы можете multicast что-то от клиента и иметь сервер (ы), чтобы ответить его IP? Или, если это не сработает, сделайте так, чтобы сервер многоадресная рассылка его IP через определенный интервал времени сделал это. Извините за отсутствие информации, я только что узнал об этом :)

1 голос
/ 09 сентября 2011

вы не можете использовать тот же метод, что и при получении вашего ip.

разрешить клиенту отправлять широковещательную рассылку - если нет ответа, подождите
сервер получит широковещательную рассылку и отправит одну обратно со своим собственным ip.
теперь клиент знает, что сервер там и по какому ip.

1 голос
/ 09 сентября 2011

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

Идея довольно проста: сервер, на котором сервер вашего приложения может зарегистрироваться, и где ваши клиенты приложений могут получить список активныхсерверы.

  1. Сервер A загружен и немедленно отправляет приветственное сообщение на главный сервер
  2. Сервер B загружается и отправляет приветственное сообщение на главный сервер
  3. И сервер A, и B продолжают отправлять приветственные сообщения на главный сервер каждые X минут, поэтому он знает, что они все еще на
  4. Клиент A загружен - необходимо выполнить команду - запрашивает у главного сервера список активных серверов - выбирает сервер изкоманда list - Issues

Следует иметь в виду:

  1. Главный сервер должен находиться по известному адресу / порту - либо фиксированный ip, либо get ip throw хорошо известным ServerName
  2. Назначение главного сервера - просто зарегистрировать серверы и предоставить клиентам их адреса. На первый взгляд, я не вижу других сервисов, которые онпредоставьте ваше приложение

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

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

0 голосов
/ 10 сентября 2011

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

0 голосов
/ 02 сентября 2011

ICMP Ping не определяет, прослушивает ли компьютер определенный порт, только если компьютер настроен на ответ на эхо-запрос.ICMP - это протокол, отличный от TCP или UDP.Это только для вас, чтобы определить, используется ли IP-адрес, и даже тогда становится менее жизнеспособным.

У вас есть два варианта.

  1. Пусть клиент постоянно проверяет каждый IP-адрес в вашей локальной сети и пытается открыть порт 2121. Это не очень хороший вариант.

  2. Пусть каждый сервер отправляет пинг ICMP на широковещательный адрес с конкретными данными, объявляющими, что он включен (и, возможно, не подключен к клиенту), но никогда не часто (я бы рекомендовал минутудля тестирования и минимум 5 минут для производства).Все, что должно сделать ваше программное обеспечение - это найти широковещательный пинг и подключиться к отправляющему IP-адресу.

Обновление:

using System.Net.NetworkInformation;


private Ping _Ping = new Ping();
private PingOptions _PingOptions = new PingOptions(64, true);
private byte[] _PingID = Encoding.ASCII.GetBytes("MyPingID");
private _PingResponse = new AutoResetEvent(false);

public <classname> //Constructor
{
  _Ping.PingCompleted += new PingCompletedEventHander(PingCompleted);
}

public void PingCompleted(object Sender, PingCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        //Status Unknown;
    }
    else if (e.Error != null)
    {
        //Status Error;
    }
    else if (e.Reply.Status == IPStatus.Success)
    {
        // Device Replying
    }
    else
    {
        // Status Unknown
    }
}

public void StartPing(string AddressToPing)
{
  IPAddress ipAddress = IPAddress.Parse(AddressToPing);
  _Ping.SendAsync(ipAddress, 15000, _PingID, _PingOptions, _PingResponse);
}
0 голосов
/ 02 сентября 2011

Я полагаю, у вас есть один сервер.Если вы можете гарантировать, что местоположение сервера (IP-адрес и порт) является постоянным (или может быть просмотрено), то каждое клиентское приложение может «зарегистрироваться» на сервере, подключившись к нему и сообщив серверу об IP-адресе и локальном порту дляперезвоните.

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