C # Async Ping: как избежать исключения нехватки памяти? - PullRequest
1 голос
/ 23 августа 2010

Вопрос. Я хочу найти в подсети все компьютеры в ней.Поэтому я отправляю ping на все IP-адреса в подсети.

Проблема в том, что все работает нормально, если я сканирую только 192.168.0. ". Но если я сканирую 192.168. . *", тогда я получаю исключение "Недостаточно памяти".

Почему?Нужно ли ограничивать потоки, или проблема в том, что память, используемая новым пингом, не разрушается после завершения, или мне нужно вызвать gc.collect ()?

    static void Main(string[] args)
    { 
        string strFromIP = "192.168.0.1";
        string strToIP = "192.168.255.255";

        Oyster.Math.IntX omiFromIP = 0;
        Oyster.Math.IntX omiToIP = 0;
        IsValidIP(strFromIP, ref omiFromIP);
        IsValidIP(strToIP, ref omiToIP);
        for (Oyster.Math.IntX omiThisIP = omiFromIP; omiThisIP <= omiToIP; ++omiThisIP)
        {
            Console.WriteLine(IPn2IPv4(omiThisIP));
            System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiThisIP));
            SendPingAsync(sniIPaddress);
        }

        Console.WriteLine(" --- Press any key to continue --- ");
        Console.ReadKey();
    } // Main


    // http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
    // http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
    // http://www.kloth.net/services/iplocate.php
    // http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
    // /1148894/spravka-po-klassu-ping-sendasync
    public static void SendPingAsync(System.Net.IPAddress sniIPaddress)
    {
        int iTimeout = 5000;
        System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
        System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();

        System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
        myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
        string data = "ABC";
        byte[] dataBuffer = Encoding.ASCII.GetBytes(data);

        parmPing.DontFragment = true;
        parmPing.Ttl = 32;

        myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
        //waiter.WaitOne();
    }


    private static void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
    {

        System.Net.NetworkInformation.PingReply reply = e.Reply;
        ((System.Threading.AutoResetEvent)e.UserState).Set();
        if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
        }
    }

Ответы [ 6 ]

2 голосов
/ 23 августа 2010

Согласно этот поток , System.Net.NetworkInformation.Ping, по-видимому, выделяет один поток на асинхронный запрос, и "очистка сети ping за счет ping создает 100 потоков и в конечном итоге приводит к нехватке памятиошибка. "

Обходной путь, который использовал этот человек, - написать свою собственную реализацию с использованием необработанных сокетов.Конечно, делать это не нужно в F #, но в этом есть ряд преимуществ.

2 голосов
/ 23 августа 2010

Первый: запуск только с 1000 пингов в первый раз (в цикле в Main)

Второй: перемещение следующих параметров в класс Программы (переменные-члены)

Oyster.Math.IntX omiFromIP = 0; 
Oyster.Math.IntX omiToIP = 0;
Oyster.Math.IntX omiCurrentIp = 0;
object syncLock = new object();

Третий:В AsyncPingCompleted сделайте что-то вроде этого внизу:

public void AsyncPingCompleted (bla bla bla)
{
    //[..other code..]

    lock (syncLock) 
    {
        if (omiToIP < omiCurrentIp)
        {
           ++omiCurrentIp;
           System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiCurrentIp)); 
           SendPingAsync(sniIPaddress); 
        }
    }
}

Обновление с полным примером кода

public class Example
{
    // Number of pings that can be pending at the same time
    private const int InitalRequests = 10000;

    // variables from your Main method
    private Oyster.Math.IntX _omiFromIP = 0;
    private Oyster.Math.IntX _omiToIP = 0;
    private Oyster.Math.IntX _omiCurrentIp = 0;

    // synchronoize so that two threads
    // cannot ping the same IP.
    private object _syncLock = new object();

    static void Main(string[] args)
    {
        string strFromIP = "192.168.0.1";
        string strToIP = "192.168.255.255";

        IsValidIP(strFromIP, ref _omiFromIP);
        IsValidIP(strToIP, ref _omiToIP);
        for (_omiCurrentIp = _omiFromIP; _omiCurrentIp <= _omiFromIP + InitalRequests; ++_omiCurrentIp)
        {
            Console.WriteLine(IPn2IPv4(_omiCurrentIp));
            System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
            SendPingAsync(sniIPaddress);
        }

        Console.WriteLine(" --- Press any key to continue --- ");
        Console.ReadKey();
    } // Main


    // http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
    // http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
    // http://www.kloth.net/services/iplocate.php
    // http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
    // /1148894/spravka-po-klassu-ping-sendasync
    public void SendPingAsync(System.Net.IPAddress sniIPaddress)
    {
        int iTimeout = 5000;
        System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
        System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();

        System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
        myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
        string data = "ABC";
        byte[] dataBuffer = Encoding.ASCII.GetBytes(data);

        parmPing.DontFragment = true;
        parmPing.Ttl = 32;

        myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
        //waiter.WaitOne();
    }


    private void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
    {

        System.Net.NetworkInformation.PingReply reply = e.Reply;
        ((System.Threading.AutoResetEvent)e.UserState).Set();
        if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
        }


        // Keep starting those async pings until all ips have been invoked.
        lock (_syncLock)
        {
            if (_omiToIP < _omiCurrentIp)
            {
                ++_omiCurrentIp;
                System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
                SendPingAsync(sniIPaddress);
            }
        }
    }        
}
1 голос
/ 17 августа 2013

Я сделал что-то похожее на это. Способ решения проблемы в моем проекте состоял в том, чтобы привести экземпляр ping к IDisposable:

(myPing as IDisposable).Dispose()

Итак, получите список, скажем, 254 экземпляров ping, выполняющихся асинхронно (XXX1 / 254), и отследите, когда все они сообщили об этом. Когда они появятся, выполните итерацию по списку экземпляров ping, запустите вышеуказанный код экземпляр, а затем вывести список.

Работает как шарм.

1 голос
/ 23 августа 2010

Полагаю, проблема в том, что вы порождаете примерно 63K пинг-запросов почти одновременно .Без дальнейшего профилирования памяти трудно сказать, какие части потребляют память.Вы работаете с сетевыми ресурсами, которые, вероятно, ограничены.Регулирование количества активных эхо-запросов облегчит использование локальных ресурсов, а также сетевого трафика.

Снова я бы рассмотрел библиотеку параллельных задач , конструкцию Parallel.For в сочетании с Task<T> должен облегчить вам задачу.

Примечание : для пользователей .Net 3.5 есть надежда .

0 голосов
/ 25 августа 2010

Наконец ... Пинг вообще не требуется ...

http://www.codeproject.com/KB/cs/c__ip_scanner.aspx

Все, что мне нужно было сделать, это сделать его потокобезопасным для отладки. Изменение Добавить к:

void Add( string m )
{
    Invoke(new MethodInvoker(
        delegate
        {
            add.Items.Add(m);
        }));
    //add.Items.Add( m );
}

Private Sub Add(m As String)
    Invoke(New MethodInvoker(Function() Do
        add.Items.Add(m)
    End Function))
    'add.Items.Add(m);'
End Sub
0 голосов
/ 23 августа 2010

псевдокод

do

if pings_running > 100 then
sleep 100ms.
else
start ping
endif

loop while morepings
...